Ejemplo n.º 1
0
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: %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 or global_ctx._interfaces:
        external_contracts = parse_external_contracts(external_contracts,
                                                      global_ctx)
    # 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  # noqa: W504
                        not func_sig.private and  # noqa: W504
                        funcs_left[sig].output_type == func_sig.output_type):
                    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 = [
                str(func_sig) 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)
Ejemplo n.º 2
0
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)
    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,
        [Expr(arg, context).lll_node 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
Ejemplo n.º 3
0
def pack_logging_data(expected_data, args, context, pos):
    # Checks to see if there's any data
    if not args:
        return ["seq"], 0, None, 0
    holder = ["seq"]
    maxlen = len(args) * 32  # total size of all packed args (upper limit)

    # Unroll any function calls, to temp variables.
    prealloacted = {}
    for idx, (arg, _expected_arg) in enumerate(zip(args, expected_data)):

        if isinstance(arg, (vy_ast.Str, vy_ast.Call)) and arg.get("func.id") != "empty":
            expr = Expr(arg, context)
            source_lll = expr.lll_node
            typ = source_lll.typ

            if isinstance(arg, vy_ast.Str):
                if len(arg.s) > typ.maxlen:
                    raise TypeMismatch(f"Data input bytes are to big: {len(arg.s)} {typ}", pos)

            tmp_variable = context.new_internal_variable(source_lll.typ)
            tmp_variable_node = LLLnode.from_list(
                tmp_variable,
                typ=source_lll.typ,
                pos=getpos(arg),
                location="memory",
                annotation=f"log_prealloacted {source_lll.typ}",
            )
            # Store len.
            # holder.append(['mstore', len_placeholder, ['mload', unwrap_location(source_lll)]])
            # Copy bytes.

            holder.append(
                make_setter(tmp_variable_node, source_lll, pos=getpos(arg), location="memory")
            )
            prealloacted[idx] = tmp_variable_node

    # Create internal variables for for dynamic and static args.
    static_types = []
    for data in expected_data:
        static_types.append(data.typ if not isinstance(data.typ, ByteArrayLike) else BaseType(32))

    requires_dynamic_offset = any(isinstance(data.typ, ByteArrayLike) for data in expected_data)

    dynamic_offset_counter = None
    if requires_dynamic_offset:
        dynamic_offset_counter = context.new_internal_variable(BaseType(32))
        dynamic_placeholder = context.new_internal_variable(BaseType(32))

    static_vars = [context.new_internal_variable(i) for i in static_types]

    # Populate static placeholders.
    for i, (arg, data) in enumerate(zip(args, expected_data)):
        typ = data.typ
        placeholder = static_vars[i]
        if not isinstance(typ, ByteArrayLike):
            holder, maxlen = pack_args_by_32(
                holder, maxlen, prealloacted.get(i, arg), typ, context, placeholder, pos=pos,
            )

    # Dynamic position starts right after the static args.
    if requires_dynamic_offset:
        holder.append(LLLnode.from_list(["mstore", dynamic_offset_counter, maxlen]))

    # Calculate maximum dynamic offset placeholders, used for gas estimation.
    for _arg, data in zip(args, expected_data):
        typ = data.typ
        if isinstance(typ, ByteArrayLike):
            maxlen += 32 + ceil32(typ.maxlen)

    if requires_dynamic_offset:
        datamem_start = dynamic_placeholder + 32
    else:
        datamem_start = static_vars[0]

    # Copy necessary data into allocated dynamic section.
    for i, (arg, data) in enumerate(zip(args, expected_data)):
        typ = data.typ
        if isinstance(typ, ByteArrayLike):
            if isinstance(arg, vy_ast.Call) and arg.func.get("id") == "empty":
                # TODO add support for this
                raise StructureException(
                    "Cannot use `empty` on Bytes or String types within an event log", arg
                )
            pack_args_by_32(
                holder=holder,
                maxlen=maxlen,
                arg=prealloacted.get(i, arg),
                typ=typ,
                context=context,
                placeholder=static_vars[i],
                datamem_start=datamem_start,
                dynamic_offset_counter=dynamic_offset_counter,
                pos=pos,
            )

    return holder, maxlen, dynamic_offset_counter, datamem_start
Ejemplo n.º 4
0
    def parse_for_list(self):
        from .parser import (parse_body, make_setter)

        iter_list_node = Expr(self.stmt.iter, self.context).lll_node
        if not isinstance(iter_list_node.typ.subtype,
                          BaseType):  # Sanity check on list subtype.
            raise StructureException(
                'For loops allowed only on basetype lists.', self.stmt.iter)
        iter_var_type = self.context.vars.get(
            self.stmt.iter.id).typ if isinstance(self.stmt.iter,
                                                 ast.Name) else None
        subtype = iter_list_node.typ.subtype.typ
        varname = self.stmt.target.id
        value_pos = self.context.new_variable(varname, BaseType(subtype))
        i_pos = self.context.new_variable('_index_for_' + varname,
                                          BaseType(subtype))
        self.context.forvars[varname] = True
        if iter_var_type:  # Is a list that is already allocated to memory.
            self.context.set_in_for_loop(
                self.stmt.iter.id
            )  # make sure list cannot be altered whilst iterating.
            iter_var = self.context.vars.get(self.stmt.iter.id)
            body = [
                'seq',
                [
                    'mstore', value_pos,
                    [
                        'mload',
                        ['add', iter_var.pos, ['mul', ['mload', i_pos], 32]]
                    ]
                ],
                parse_body(self.stmt.body, self.context)
            ]
            o = LLLnode.from_list(['repeat', i_pos, 0, iter_var.size, body],
                                  typ=None,
                                  pos=getpos(self.stmt))
            self.context.remove_in_for_loop(self.stmt.iter.id)
        elif isinstance(self.stmt.iter,
                        ast.List):  # List gets defined in the for statement.
            # Allocate list to memory.
            count = iter_list_node.typ.count
            tmp_list = LLLnode.from_list(obj=self.context.new_placeholder(
                ListType(iter_list_node.typ.subtype, count)),
                                         typ=ListType(
                                             iter_list_node.typ.subtype,
                                             count),
                                         location='memory')
            setter = make_setter(tmp_list,
                                 iter_list_node,
                                 'memory',
                                 pos=getpos(self.stmt))
            body = [
                'seq',
                [
                    'mstore', value_pos,
                    [
                        'mload',
                        ['add', tmp_list, ['mul', ['mload', i_pos], 32]]
                    ]
                ],
                parse_body(self.stmt.body, self.context)
            ]
            o = LLLnode.from_list(
                ['seq', setter, ['repeat', i_pos, 0, count, body]],
                typ=None,
                pos=getpos(self.stmt))
        elif isinstance(self.stmt.iter,
                        ast.Attribute):  # List is contained in storage.
            count = iter_list_node.typ.count
            self.context.set_in_for_loop(
                iter_list_node.annotation
            )  # make sure list cannot be altered whilst iterating.
            body = [
                'seq',
                [
                    'mstore', value_pos,
                    [
                        'sload',
                        ['add', ['sha3_32', iter_list_node], ['mload', i_pos]]
                    ]
                ],
                parse_body(self.stmt.body, self.context),
            ]
            o = LLLnode.from_list(['seq', ['repeat', i_pos, 0, count, body]],
                                  typ=None,
                                  pos=getpos(self.stmt))
            self.context.remove_in_for_loop(iter_list_node.annotation)
        del self.context.vars[varname]
        del self.context.vars['_index_for_' + varname]
        del self.context.forvars[varname]
        return o
Ejemplo n.º 5
0
    def from_definition(cls,
                        code,
                        sigs=None,
                        custom_structs=None,
                        contract_def=False,
                        constants=None,
                        constant_override=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

        const = constant_override
        payable = False
        private = False
        public = False
        nonreentrant_key = ''

        # Update function properties from decorators
        for dec in code.decorator_list:
            if isinstance(dec, vy_ast.Name) and dec.id == "constant":
                const = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "payable":
                payable = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "private":
                private = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "public":
                public = 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 public and private:
            raise StructureException(
                f"Cannot use public and private decorators on the same function: {name}"
            )
        if payable and const:
            raise StructureException(
                f"Function {name} cannot be both constant and payable.")
        if payable and private:
            raise StructureException(
                f"Function {name} cannot be both private and payable.")
        if (not public and not private) and not contract_def:
            raise StructureException(
                "Function visibility must be declared (@public or @private)",
                code,
            )
        if const and nonreentrant_key:
            raise StructureException(
                "@nonreentrant makes no sense on a @constant 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, const, payable, private,
                   nonreentrant_key, sig, method_id, code)
Ejemplo n.º 6
0
def pack_arguments(signature,
                   args,
                   context,
                   stmt_expr,
                   return_placeholder=True):
    pos = getpos(stmt_expr)
    placeholder_typ = ByteArrayType(
        maxlen=sum([get_size_of_type(arg.typ)
                    for arg in signature.args]) * 32 + 32)
    placeholder = context.new_placeholder(placeholder_typ)
    setters = [['mstore', placeholder, signature.method_id]]
    needpos = False
    staticarray_offset = 0
    expected_arg_count = len(signature.args)
    actual_arg_count = len(args)
    if actual_arg_count != expected_arg_count:
        raise StructureException(
            f"Wrong number of args for: {signature.name} "
            f"({actual_arg_count} args given, expected {expected_arg_count}",
            stmt_expr)

    for i, (arg,
            typ) in enumerate(zip(args, [arg.typ for arg in signature.args])):
        if isinstance(typ, BaseType):
            setters.append(
                make_setter(LLLnode.from_list(
                    placeholder + staticarray_offset + 32 + i * 32,
                    typ=typ,
                ),
                            arg,
                            'memory',
                            pos=pos,
                            in_function_call=True))

        elif isinstance(typ, ByteArrayLike):
            setters.append([
                'mstore', placeholder + staticarray_offset + 32 + i * 32,
                '_poz'
            ])
            arg_copy = LLLnode.from_list('_s',
                                         typ=arg.typ,
                                         location=arg.location)
            target = LLLnode.from_list(
                ['add', placeholder + 32, '_poz'],
                typ=typ,
                location='memory',
            )
            setters.append([
                'with',
                '_s',
                arg,
                [
                    'seq',
                    make_byte_array_copier(target, arg_copy, pos),
                    [
                        'set', '_poz',
                        [
                            'add', 32,
                            ['ceil32', ['add', '_poz',
                                        get_length(arg_copy)]]
                        ]
                    ],
                ],
            ])
            needpos = True

        elif isinstance(typ, (StructType, ListType)):
            if has_dynamic_data(typ):
                raise TypeMismatchException("Cannot pack bytearray in struct",
                                            stmt_expr)
            target = LLLnode.from_list(
                [placeholder + 32 + staticarray_offset + i * 32],
                typ=typ,
                location='memory',
            )
            setters.append(make_setter(target, arg, 'memory', pos=pos))
            if (isinstance(typ, ListType)):
                count = typ.count
            else:
                count = len(typ.tuple_items())
            staticarray_offset += 32 * (count - 1)

        else:
            raise TypeMismatchException(f"Cannot pack argument of type {typ}",
                                        stmt_expr)

    # For private call usage, doesn't use a returner.
    returner = [[placeholder + 28]] if return_placeholder else []
    if needpos:
        return (LLLnode.from_list([
            'with', '_poz',
            len(args) * 32 + staticarray_offset, ['seq'] + setters + returner
        ],
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
    else:
        return (LLLnode.from_list(['seq'] + setters + returner,
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
Ejemplo n.º 7
0
    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 VariableDeclarationException(
                    "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 VariableDeclarationException(
                    "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 VariableDeclarationException(
                    "%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)
Ejemplo n.º 8
0
    def call(self):
        from .parser import (
            external_contract_call_expr,
            pack_arguments,
        )
        from vyper.functions import (
            dispatch_table, )
        if isinstance(self.expr.func, ast.Name):
            function_name = self.expr.func.id
            if function_name in dispatch_table:
                return dispatch_table[function_name](self.expr, self.context)
            else:
                err_msg = "Not a top-level function: {}".format(function_name)
                if function_name in self.context.sigs['self']:
                    err_msg += ". Did you mean self.{}?".format(function_name)
                raise StructureException(err_msg, self.expr)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(
                self.expr.func.value,
                ast.Name) and self.expr.func.value.id == "self":
            method_name = self.expr.func.attr
            if method_name not in self.context.sigs['self']:
                raise VariableDeclarationException(
                    "Function not declared yet (reminder: functions cannot "
                    "call functions later in code than themselves): %s" %
                    self.expr.func.attr)

            sig = self.context.sigs['self'][method_name]
            if self.context.is_constant and not sig.const:
                raise ConstancyViolationException(
                    "May not call non-constant function '%s' within a constant function."
                    % (method_name), getpos(self.expr))
            add_gas = self.context.sigs['self'][method_name].gas  # gas of call
            inargs, inargsize = pack_arguments(
                sig,
                [Expr(arg, self.context).lll_node
                 for arg in self.expr.args], self.context)
            output_placeholder = self.context.new_placeholder(
                typ=sig.output_type)
            if isinstance(sig.output_type, BaseType):
                returner = output_placeholder
            elif isinstance(sig.output_type, ByteArrayType):
                returner = output_placeholder + 32
            else:
                raise TypeMismatchException(
                    "Invalid output type: %r" % sig.output_type, self.expr)
            o = LLLnode.from_list([
                'seq',
                [
                    'assert',
                    [
                        'call', ['gas'], ['address'], 0, inargs, inargsize,
                        output_placeholder,
                        get_size_of_type(sig.output_type) * 32
                    ]
                ], returner
            ],
                                  typ=sig.output_type,
                                  location='memory',
                                  pos=getpos(self.expr),
                                  add_gas_estimate=add_gas,
                                  annotation='Internal Call: %s' % method_name)
            o.gas += sig.gas
            return o
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(
                self.expr.func.value, ast.Call):
            contract_name = self.expr.func.value.func.id
            contract_address = Expr.parse_value_expr(
                self.expr.func.value.args[0], self.context)
            return external_contract_call_expr(self.expr, self.context,
                                               contract_name, contract_address)
        elif isinstance(self.expr.func.value, ast.Attribute
                        ) and self.expr.func.value.attr in self.context.sigs:
            contract_name = self.expr.func.value.attr
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.expr),
                                  annotation='self.' +
                                  self.expr.func.value.attr))
            return external_contract_call_expr(self.expr, self.context,
                                               contract_name, contract_address)
        elif isinstance(
                self.expr.func.value, ast.Attribute
        ) and self.expr.func.value.attr in self.context.globals:
            contract_name = self.context.globals[
                self.expr.func.value.attr].typ.unit
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.expr),
                                  annotation='self.' +
                                  self.expr.func.value.attr))
            return external_contract_call_expr(self.expr, self.context,
                                               contract_name, contract_address)
        else:
            raise StructureException(
                "Unsupported operator: %r" % ast.dump(self.expr), self.expr)
Ejemplo n.º 9
0
def pack_arguments(signature, args, context):
    placeholder_typ = ByteArrayType(
        maxlen=sum([get_size_of_type(arg.typ)
                    for arg in signature.args]) * 32 + 32)
    placeholder = context.new_placeholder(placeholder_typ)
    setters = [['mstore', placeholder, signature.method_id]]
    needpos = False
    staticarray_offset = 0
    expected_arg_count = len(signature.args)
    actual_arg_count = len(args)
    if actual_arg_count != expected_arg_count:
        raise StructureException(
            "Wrong number of args for: %s (%s args, expected %s)" %
            (signature.name, actual_arg_count, expected_arg_count))

    for i, (arg,
            typ) in enumerate(zip(args, [arg.typ for arg in signature.args])):
        if isinstance(typ, BaseType):
            setters.append(
                make_setter(
                    LLLnode.from_list(placeholder + staticarray_offset + 32 +
                                      i * 32,
                                      typ=typ), arg, 'memory'))
        elif isinstance(typ, ByteArrayType):
            setters.append([
                'mstore', placeholder + staticarray_offset + 32 + i * 32,
                '_poz'
            ])
            arg_copy = LLLnode.from_list('_s',
                                         typ=arg.typ,
                                         location=arg.location)
            target = LLLnode.from_list(['add', placeholder + 32, '_poz'],
                                       typ=typ,
                                       location='memory')
            setters.append([
                'with', '_s', arg,
                [
                    'seq',
                    make_byte_array_copier(target, arg_copy),
                    [
                        'set', '_poz',
                        ['add', 32, ['add', '_poz',
                                     get_length(arg_copy)]]
                    ]
                ]
            ])
            needpos = True
        elif isinstance(typ, ListType):
            target = LLLnode.from_list(
                [placeholder + 32 + staticarray_offset + i * 32],
                typ=typ,
                location='memory')
            setters.append(make_setter(target, arg, 'memory'))
            staticarray_offset += 32 * (typ.count - 1)
        else:
            raise TypeMismatchException("Cannot pack argument of type %r" %
                                        typ)
    if needpos:
        return LLLnode.from_list(['with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + [placeholder + 28]],
                                 typ=placeholder_typ, location='memory'), \
            placeholder_typ.maxlen - 28
    else:
        return LLLnode.from_list(['seq'] + setters + [placeholder + 28], typ=placeholder_typ, location='memory'), \
            placeholder_typ.maxlen - 28
Ejemplo n.º 10
0
    def compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.comparators[0], self.context)

        if isinstance(right.typ, NullType):
            raise InvalidLiteralException(
                'Comparison to None is not allowed, compare against a default value.',
                self.expr,
            )

        if isinstance(left.typ, ByteArrayLike) and isinstance(right.typ, ByteArrayLike):
            # TODO: Can this if branch be removed ^
            pass

        elif isinstance(self.expr.ops[0], ast.In) and isinstance(right.typ, ListType):
            if left.typ != right.typ.subtype:
                raise TypeMismatchException(
                    "Can't use IN comparison with different types!",
                    self.expr,
                )
            return self.build_in_comparator()
        else:
            if not are_units_compatible(left.typ, right.typ) and not are_units_compatible(right.typ, left.typ):  # noqa: E501
                raise TypeMismatchException("Can't compare values with different units!", self.expr)

        if len(self.expr.ops) != 1:
            raise StructureException(
                "Cannot have a comparison with more than two elements",
                self.expr,
            )
        if isinstance(self.expr.ops[0], ast.Gt):
            op = 'sgt'
        elif isinstance(self.expr.ops[0], ast.GtE):
            op = 'sge'
        elif isinstance(self.expr.ops[0], ast.LtE):
            op = 'sle'
        elif isinstance(self.expr.ops[0], ast.Lt):
            op = 'slt'
        elif isinstance(self.expr.ops[0], ast.Eq):
            op = 'eq'
        elif isinstance(self.expr.ops[0], ast.NotEq):
            op = 'ne'
        else:
            raise Exception("Unsupported comparison operator")

        # Compare (limited to 32) byte arrays.
        if isinstance(left.typ, ByteArrayLike) and isinstance(right.typ, ByteArrayLike):
            left = Expr(self.expr.left, self.context).lll_node
            right = Expr(self.expr.comparators[0], self.context).lll_node

            length_mismatch = (left.typ.maxlen != right.typ.maxlen)
            left_over_32 = left.typ.maxlen > 32
            right_over_32 = right.typ.maxlen > 32
            if length_mismatch or left_over_32 or right_over_32:
                left_keccak = keccak256_helper(self.expr, [left], None, self.context)
                right_keccak = keccak256_helper(self.expr, [right], None, self.context)

                if op == 'eq' or op == 'ne':
                    return LLLnode.from_list(
                        [op, left_keccak, right_keccak],
                        typ='bool',
                        pos=getpos(self.expr),
                    )

                else:
                    raise ParserException(
                        "Can only compare strings/bytes of length shorter",
                        " than 32 bytes other than equality comparisons",
                        self.expr,
                    )

            else:
                def load_bytearray(side):
                    if side.location == 'memory':
                        return ['mload', ['add', 32, side]]
                    elif side.location == 'storage':
                        return ['sload', ['add', 1, ['sha3_32', side]]]

                return LLLnode.from_list(
                    [op, load_bytearray(left), load_bytearray(right)],
                    typ='bool',
                    pos=getpos(self.expr),
                )

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ('eq', 'ne'):
                raise TypeMismatchException("Invalid type for comparison op", self.expr)
        left_type, right_type = left.typ.typ, right.typ.typ

        # Special Case: comparison of a literal integer. If in valid range allow it to be compared.
        if {left_type, right_type} == {'int128', 'uint256'} and {left.typ.is_literal, right.typ.is_literal} == {True, False}:  # noqa: E501

            comparison_allowed = False
            if left.typ.is_literal and SizeLimits.in_bounds(right_type, left.value):
                comparison_allowed = True
            elif right.typ.is_literal and SizeLimits.in_bounds(left_type, right.value):
                comparison_allowed = True
            op = self._signed_to_unsigned_comparision_op(op)

            if comparison_allowed:
                return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))

        elif {left_type, right_type} == {'uint256', 'uint256'}:
            op = self._signed_to_unsigned_comparision_op(op)
        elif (left_type in ('decimal', 'int128') or right_type in ('decimal', 'int128')) and left_type != right_type:  # noqa: E501
            raise TypeMismatchException(
                f'Implicit conversion from {left_type} to {right_type} disallowed, please convert.',
                self.expr,
            )

        if left_type == right_type:
            return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))
        else:
            raise TypeMismatchException(
                f"Unsupported types for comparison: {left_type} {right_type}",
                self.expr,
            )
Ejemplo n.º 11
0
    def call(self):
        from vyper.parser.parser import (external_contract_call)
        from vyper.functions import (
            dispatch_table, )

        if isinstance(self.expr.func, ast.Name):
            function_name = self.expr.func.id
            if function_name in dispatch_table:
                return dispatch_table[function_name](self.expr, self.context)
            else:
                err_msg = "Not a top-level function: {}".format(function_name)
                if function_name in [
                        x.split('(')[0]
                        for x, _ in self.context.sigs['self'].items()
                ]:
                    err_msg += ". Did you mean self.{}?".format(function_name)
                raise StructureException(err_msg, self.expr)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(
                self.expr.func.value,
                ast.Name) and self.expr.func.value.id == "self":
            return self_call.make_call(self.expr, self.context)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(
                self.expr.func.value, ast.Call):
            contract_name = self.expr.func.value.func.id
            contract_address = Expr.parse_value_expr(
                self.expr.func.value.args[0], self.context)
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.expr),
                                          value=value,
                                          gas=gas)
        elif isinstance(self.expr.func.value, ast.Attribute
                        ) and self.expr.func.value.attr in self.context.sigs:
            contract_name = self.expr.func.value.attr
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.expr),
                                  annotation='self.' +
                                  self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.expr),
                                          value=value,
                                          gas=gas)
        elif isinstance(
                self.expr.func.value, ast.Attribute
        ) and self.expr.func.value.attr in self.context.globals:
            contract_name = self.context.globals[
                self.expr.func.value.attr].typ.unit
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.expr),
                                  annotation='self.' +
                                  self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.expr),
                                          value=value,
                                          gas=gas)
        else:
            raise StructureException(
                "Unsupported operator: %r" % ast.dump(self.expr), self.expr)
Ejemplo n.º 12
0
Archivo: expr.py Proyecto: 6pakla/vyper
    def arithmetic(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            raise TypeMismatch(
                f"Unsupported types for arithmetic op: {left.typ} {right.typ}",
                self.expr,
            )

        arithmetic_pair = {left.typ.typ, right.typ.typ}

        # Special Case: Simplify any literal to literal arithmetic at compile time.
        if left.typ.is_literal and right.typ.is_literal and \
           isinstance(right.value, int) and isinstance(left.value, int) and \
           arithmetic_pair.issubset({'uint256', 'int128'}):

            if isinstance(self.expr.op, vy_ast.Add):
                val = left.value + right.value
            elif isinstance(self.expr.op, vy_ast.Sub):
                val = left.value - right.value
            elif isinstance(self.expr.op, vy_ast.Mult):
                val = left.value * right.value
            elif isinstance(self.expr.op, vy_ast.Pow):
                val = left.value ** right.value
            elif isinstance(self.expr.op, (vy_ast.Div, vy_ast.Mod)):
                if right.value == 0:
                    raise ZeroDivisionException(
                        "integer division or modulo by zero",
                        self.expr,
                    )
                if isinstance(self.expr.op, vy_ast.Div):
                    val = left.value // right.value
                elif isinstance(self.expr.op, vy_ast.Mod):
                    # modified modulo logic to remain consistent with EVM behaviour
                    val = abs(left.value) % abs(right.value)
                    if left.value < 0:
                        val = -val
            else:
                raise StructureException(
                    f'Unsupported literal operator: {type(self.expr.op)}',
                    self.expr,
                )

            num = vy_ast.Int(value=val)
            num.full_source_code = self.expr.full_source_code
            num.node_source_code = self.expr.node_source_code
            num.lineno = self.expr.lineno
            num.col_offset = self.expr.col_offset
            num.end_lineno = self.expr.end_lineno
            num.end_col_offset = self.expr.end_col_offset

            return Expr.parse_value_expr(num, self.context)

        pos = getpos(self.expr)

        # Special case with uint256 were int literal may be casted.
        if arithmetic_pair == {'uint256', 'int128'}:
            # Check right side literal.
            if right.typ.is_literal and SizeLimits.in_bounds('uint256', right.value):
                right = LLLnode.from_list(
                    right.value,
                    typ=BaseType('uint256', None, is_literal=True),
                    pos=pos,
                )

            # Check left side literal.
            elif left.typ.is_literal and SizeLimits.in_bounds('uint256', left.value):
                left = LLLnode.from_list(
                    left.value,
                    typ=BaseType('uint256', None, is_literal=True),
                    pos=pos,
                )

        if left.typ.typ == "decimal" and isinstance(self.expr.op, vy_ast.Pow):
            raise TypeMismatch(
                "Cannot perform exponentiation on decimal values.",
                self.expr,
            )

        # Only allow explicit conversions to occur.
        if left.typ.typ != right.typ.typ:
            raise TypeMismatch(
                f"Cannot implicitly convert {left.typ.typ} to {right.typ.typ}.",
                self.expr,
            )

        ltyp, rtyp = left.typ.typ, right.typ.typ
        if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)):
            new_typ = BaseType(ltyp)
            op = 'add' if isinstance(self.expr.op, vy_ast.Add) else 'sub'

            if ltyp == 'uint256' and isinstance(self.expr.op, vy_ast.Add):
                # safeadd
                arith = ['seq',
                         ['assert', ['ge', ['add', 'l', 'r'], 'l']],
                         ['add', 'l', 'r']]

            elif ltyp == 'uint256' and isinstance(self.expr.op, vy_ast.Sub):
                # safesub
                arith = ['seq',
                         ['assert', ['ge', 'l', 'r']],
                         ['sub', 'l', 'r']]

            elif ltyp == rtyp:
                arith = [op, 'l', 'r']

            else:
                raise Exception(f"Unsupported Operation '{op}({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Mult):
            new_typ = BaseType(ltyp)
            if ltyp == rtyp == 'uint256':
                arith = ['with', 'ans', ['mul', 'l', 'r'],
                         ['seq',
                             ['assert',
                                 ['or',
                                     ['eq', ['div', 'ans', 'l'], 'r'],
                                     ['iszero', 'l']]],
                             'ans']]

            elif ltyp == rtyp == 'int128':
                # TODO should this be 'smul' (note edge cases in YP for smul)
                arith = ['mul', 'l', 'r']

            elif ltyp == rtyp == 'decimal':
                # TODO should this be smul
                arith = ['with', 'ans', ['mul', 'l', 'r'],
                         ['seq',
                             ['assert',
                                 ['or',
                                     ['eq', ['sdiv', 'ans', 'l'], 'r'],
                                     ['iszero', 'l']]],
                             ['sdiv', 'ans', DECIMAL_DIVISOR]]]
            else:
                raise Exception(f"Unsupported Operation 'mul({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Div):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot divide by 0.", self.expr)

            new_typ = BaseType(ltyp)
            if ltyp == rtyp == 'uint256':
                arith = ['div', 'l', ['clamp_nonzero', 'r']]

            elif ltyp == rtyp == 'int128':
                arith = ['sdiv', 'l', ['clamp_nonzero', 'r']]

            elif ltyp == rtyp == 'decimal':
                arith = ['sdiv',
                         # TODO check overflow cases, also should it be smul
                         ['mul', 'l', DECIMAL_DIVISOR],
                         ['clamp_nonzero', 'r']]

            else:
                raise Exception(f"Unsupported Operation 'div({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot calculate modulus of 0.", self.expr)

            new_typ = BaseType(ltyp)

            if ltyp == rtyp == 'uint256':
                arith = ['mod', 'l', ['clamp_nonzero', 'r']]
            elif ltyp == rtyp:
                # TODO should this be regular mod
                arith = ['smod', 'l', ['clamp_nonzero', 'r']]

            else:
                raise Exception(f"Unsupported Operation 'mod({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, vy_ast.Pow):
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(self.expr.right, vy_ast.Name):
                raise TypeMismatch(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr,
                )
            new_typ = BaseType(ltyp)

            if ltyp == rtyp == 'uint256':
                arith = ['seq',
                         ['assert',
                             ['or',
                                 # r == 1 | iszero(r)
                                 # could be simplified to ~(r & 1)
                                 ['or', ['eq', 'r', 1], ['iszero', 'r']],
                                 ['lt', 'l', ['exp', 'l', 'r']]]],
                         ['exp', 'l', 'r']]
            elif ltyp == rtyp == 'int128':
                arith = ['exp', 'l', 'r']

            else:
                raise TypeMismatch('Only whole number exponents are supported', self.expr)
        else:
            raise StructureException(f"Unsupported binary operator: {self.expr.op}", self.expr)

        p = ['seq']

        if new_typ.typ == 'int128':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINNUM],
                arith,
                ['mload', MemoryPositions.MAXNUM],
            ])
        elif new_typ.typ == 'decimal':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINDECIMAL],
                arith,
                ['mload', MemoryPositions.MAXDECIMAL],
            ])
        elif new_typ.typ == 'uint256':
            p.append(arith)
        else:
            raise Exception(f"{arith} {new_typ}")

        p = ['with', 'l', left, ['with', 'r', right, p]]
        return LLLnode.from_list(p, typ=new_typ, pos=pos)
Ejemplo n.º 13
0
Archivo: expr.py Proyecto: 6pakla/vyper
 def attribute(self):
     # x.balance: balance of address x
     if self.expr.attr == 'balance':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatch(
                 "Type mismatch: balance keyword expects an address as input",
                 self.expr
             )
         if (
             isinstance(self.expr.value, vy_ast.Name) and
             self.expr.value.id == "self" and
             version_check(begin="istanbul")
         ):
             seq = ['selfbalance']
         else:
             seq = ['balance', addr]
         return LLLnode.from_list(
             seq,
             typ=BaseType('uint256'),
             location=None,
             pos=getpos(self.expr),
         )
     # x.codesize: codesize of address x
     elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatch(
                 "Type mismatch: codesize keyword expects an address as input",
                 self.expr,
             )
         if self.expr.attr == 'codesize':
             eval_code = ['extcodesize', addr]
             output_type = 'int128'
         else:
             eval_code = ['gt', ['extcodesize', addr], 0]
             output_type = 'bool'
         return LLLnode.from_list(
             eval_code,
             typ=BaseType(output_type),
             location=None,
             pos=getpos(self.expr),
         )
     # x.codehash: keccak of address x
     elif self.expr.attr == 'codehash':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatch(
                 "codehash keyword expects an address as input",
                 self.expr,
             )
         if not version_check(begin="constantinople"):
             raise EvmVersionException(
                 "address.codehash is unavailable prior to constantinople ruleset",
                 self.expr
             )
         return LLLnode.from_list(
             ['extcodehash', addr],
             typ=BaseType('bytes32'),
             location=None,
             pos=getpos(self.expr)
         )
     # self.x: global attribute
     elif isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self":
         if self.expr.attr not in self.context.globals:
             raise VariableDeclarationException(
                 "Persistent variable undeclared: " + self.expr.attr,
                 self.expr,
             )
         var = self.context.globals[self.expr.attr]
         return LLLnode.from_list(
             var.pos,
             typ=var.typ,
             location='storage',
             pos=getpos(self.expr),
             annotation='self.' + self.expr.attr,
         )
     # Reserved keywords
     elif (
         isinstance(self.expr.value, vy_ast.Name) and
         self.expr.value.id in ENVIRONMENT_VARIABLES
     ):
         key = self.expr.value.id + "." + self.expr.attr
         if key == "msg.sender":
             if self.context.is_private:
                 raise StructureException(
                     "msg.sender not allowed in private functions.", self.expr
                 )
             return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr))
         elif key == "msg.value":
             if not self.context.is_payable:
                 raise NonPayableViolation(
                     "Cannot use msg.value in a non-payable function", self.expr,
                 )
             return LLLnode.from_list(
                 ['callvalue'],
                 typ=BaseType('uint256'),
                 pos=getpos(self.expr),
             )
         elif key == "msg.gas":
             return LLLnode.from_list(
                 ['gas'],
                 typ='uint256',
                 pos=getpos(self.expr),
             )
         elif key == "block.difficulty":
             return LLLnode.from_list(
                 ['difficulty'],
                 typ='uint256',
                 pos=getpos(self.expr),
             )
         elif key == "block.timestamp":
             return LLLnode.from_list(
                 ['timestamp'],
                 typ=BaseType('uint256'),
                 pos=getpos(self.expr),
             )
         elif key == "block.coinbase":
             return LLLnode.from_list(['coinbase'], typ='address', pos=getpos(self.expr))
         elif key == "block.number":
             return LLLnode.from_list(['number'], typ='uint256', pos=getpos(self.expr))
         elif key == "block.prevhash":
             return LLLnode.from_list(
                 ['blockhash', ['sub', 'number', 1]],
                 typ='bytes32',
                 pos=getpos(self.expr),
             )
         elif key == "tx.origin":
             return LLLnode.from_list(['origin'], typ='address', pos=getpos(self.expr))
         elif key == "chain.id":
             if not version_check(begin="istanbul"):
                 raise EvmVersionException(
                     "chain.id is unavailable prior to istanbul ruleset",
                     self.expr
                 )
             return LLLnode.from_list(['chainid'], typ='uint256', pos=getpos(self.expr))
         else:
             raise StructureException("Unsupported keyword: " + key, self.expr)
     # Other variables
     else:
         sub = Expr.parse_variable_location(self.expr.value, self.context)
         # contract type
         if isinstance(sub.typ, ContractType):
             return sub
         if not isinstance(sub.typ, StructType):
             raise TypeMismatch(
                 "Type mismatch: member variable access not expected",
                 self.expr.value,
             )
         attrs = list(sub.typ.members.keys())
         if self.expr.attr not in attrs:
             raise TypeMismatch(
                 f"Member {self.expr.attr} not found. Only the following available: "
                 f"{' '.join(attrs)}",
                 self.expr,
             )
         return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
Ejemplo n.º 14
0
Archivo: expr.py Proyecto: 6pakla/vyper
 def parse_variable_location(cls, expr, context):
     o = cls(expr, context).lll_node
     if not o.location:
         raise StructureException("Looking for a variable location, instead got a value", expr)
     return o
Ejemplo n.º 15
0
    def from_definition(cls, code, sigs=None, custom_units=None, contract_def=False, constant=False):
        name = code.name
        pos = 0

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

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

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(sha3(bytes(sig, 'utf-8'))[:4])
        return cls(name, args, output_type, const, payable, private, sig, method_id, custom_units)
Ejemplo n.º 16
0
def add_globals_and_events(_contracts, _defs, _events, _getters, _globals,
                           item):
    if item.value is not None:
        raise StructureException('May not assign value whilst defining type',
                                 item)
    elif isinstance(item.annotation,
                    ast.Call) and item.annotation.func.id == "event":
        if _globals or len(_defs):
            raise StructureException(
                "Events must all come before global declarations and function definitions",
                item)
        _events.append(item)
    elif not isinstance(item.target, ast.Name):
        raise StructureException(
            "Can only assign type to variable in top-level statement", item)
    # Check if variable name is reserved or invalid
    elif not is_varname_valid(item.target.id):
        raise VariableDeclarationException(
            "Variable name invalid or reserved: ", item.target)
    # Check if global already exists, if so error
    elif item.target.id in _globals:
        raise VariableDeclarationException(
            "Cannot declare a persistent variable twice!", item.target)
    elif len(_defs):
        raise StructureException(
            "Global variables must all come before function definitions", item)
    # If the type declaration is of the form public(<type here>), then proceed with
    # the underlying type but also add getters
    elif isinstance(item.annotation,
                    ast.Call) and item.annotation.func.id == "address":
        if len(item.annotation.args) != 1:
            raise StructureException("Address expects one arg (the type)")
        if item.annotation.args[0].id not in premade_contracts:
            raise VariableDeclarationException(
                "Unsupported premade contract declaration",
                item.annotation.args[0])
        premade_contract = premade_contracts[item.annotation.args[0].id]
        _contracts[item.target.id] = add_contract(premade_contract.body)
        _globals[item.target.id] = VariableRecord(item.target.id,
                                                  len(_globals),
                                                  BaseType('address'), True)
    elif isinstance(item, ast.AnnAssign) and isinstance(
            item.annotation, ast.Name) and item.annotation.id in _contracts:
        _globals[item.target.id] = VariableRecord(
            item.target.id, len(_globals),
            BaseType('address', item.annotation.id), True)
    elif isinstance(item.annotation,
                    ast.Call) and item.annotation.func.id == "public":
        if len(item.annotation.args) != 1:
            raise StructureException("Public expects one arg (the type)")
        if isinstance(item.annotation.args[0],
                      ast.Name) and item.annotation.args[0].id in _contracts:
            typ = BaseType('address', item.annotation.args[0].id)
        else:
            typ = parse_type(item.annotation.args[0], 'storage')
        _globals[item.target.id] = VariableRecord(item.target.id,
                                                  len(_globals), typ, True)
        # Adding getters here
        for getter in mk_getter(item.target.id, typ):
            _getters.append(parse_line('\n' * (item.lineno - 1) + getter))
            _getters[-1].pos = getpos(item)
    else:
        _globals[item.target.id] = VariableRecord(
            item.target.id, len(_globals),
            parse_type(item.annotation, 'storage'), True)
    return _contracts, _events, _globals, _getters
Ejemplo n.º 17
0
def pre_parse(code: str) -> Tuple[ClassTypes, str]:
    """
    Re-formats a vyper source string into a python source string and performs
    some validation.  More specifically,

    * Translates "contract" and "struct" keyword into python "class" keyword
    * Validates "@version" pragma against current compiler version
    * Prevents direct use of python "class" keyword
    * Prevents use of python semi-colon statement separator

    Also returns a mapping of detected contract and struct names to their
    respective vyper class types ("contract" or "struct").

    Parameters
    ----------
    code : str
        The vyper source code to be re-formatted.

    Returns
    -------
    dict
        Mapping of class types for the given source.
    str
        Reformatted python source string.
    """
    result = []
    previous_keyword = None
    class_types: ClassTypes = {}

    try:
        code_bytes = code.encode('utf-8')
        g = tokenize(io.BytesIO(code_bytes).readline)

        for token in g:
            toks = [token]

            typ = token.type
            string = token.string
            start = token.start
            end = token.end
            line = token.line

            if typ == COMMENT and "@version" in string:
                validate_version_pragma(string[1:], start)

            if typ == NAME and string == "class" and start[1] == 0:
                raise StructureException(
                    "The `class` keyword is not allowed. Perhaps you meant `contract` or `struct`?",
                    start,
                )

            # Make note of contract or struct name along with the type keyword
            # that preceded it
            if typ == NAME and previous_keyword is not None:
                class_types[string] = previous_keyword
                previous_keyword = None

            # Translate vyper-specific class keywords into python "class"
            # keyword
            if typ == NAME and string in VYPER_CLASS_TYPES and start[1] == 0:
                toks = [TokenInfo(NAME, "class", start, end, line)]
                previous_keyword = string

            if (typ, string) == (OP, ";"):
                raise StructureException("Semi-colon statements not allowed.",
                                         start)

            result.extend(toks)
    except TokenError as e:
        raise StructureException(e.args[0], e.args[1]) from e

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

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

    o.context = context
    o.total_gas = o.gas + calc_mem_gas(o.context.next_mem)
    o.func_name = sig.name
    return o
Ejemplo n.º 19
0
    def visit_For(self, node):
        if isinstance(node.iter, vy_ast.Subscript):
            raise StructureException("Cannot iterate over a nested list", node.iter)

        if isinstance(node.iter, vy_ast.Call):
            # iteration via range()
            if node.iter.get("func.id") != "range":
                raise IteratorException(
                    "Cannot iterate over the result of a function call", node.iter
                )
            validate_call_args(node.iter, (1, 2))

            args = node.iter.args
            if len(args) == 1:
                # range(CONSTANT)
                if not isinstance(args[0], vy_ast.Num):
                    raise StateAccessViolation("Value must be a literal", node)
                if args[0].value <= 0:
                    raise StructureException("For loop must have at least 1 iteration", args[0])
                validate_expected_type(args[0], Uint256Definition())
                type_list = get_possible_types_from_node(args[0])
            else:
                validate_expected_type(args[0], IntegerAbstractType())
                type_list = get_common_types(*args)
                if not isinstance(args[0], vy_ast.Constant):
                    # range(x, x + CONSTANT)
                    if not isinstance(args[1], vy_ast.BinOp) or not isinstance(
                        args[1].op, vy_ast.Add
                    ):
                        raise StructureException(
                            "Second element must be the first element plus a literal value",
                            args[0],
                        )
                    if not vy_ast.compare_nodes(args[0], args[1].left):
                        raise StructureException(
                            "First and second variable must be the same", args[1].left
                        )
                    if not isinstance(args[1].right, vy_ast.Int):
                        raise InvalidLiteral("Literal must be an integer", args[1].right)
                    if args[1].right.value < 1:
                        raise StructureException(
                            f"For loop has invalid number of iterations ({args[1].right.value}),"
                            " the value must be greater than zero",
                            args[1].right,
                        )
                else:
                    # range(CONSTANT, CONSTANT)
                    if not isinstance(args[1], vy_ast.Int):
                        raise InvalidType("Value must be a literal integer", args[1])
                    validate_expected_type(args[1], IntegerAbstractType())
                    if args[0].value >= args[1].value:
                        raise StructureException("Second value must be > first value", args[1])

        else:
            # iteration over a variable or literal list
            type_list = [
                i.value_type
                for i in get_possible_types_from_node(node.iter)
                if isinstance(i, ArrayDefinition)
            ]

        if not type_list:
            raise InvalidType("Not an iterable type", node.iter)

        if next((i for i in type_list if isinstance(i, ArrayDefinition)), False):
            raise StructureException("Cannot iterate over a nested list", node.iter)

        if isinstance(node.iter, (vy_ast.Name, vy_ast.Attribute)):
            # check for references to the iterated value within the body of the loop
            assign = _check_iterator_assign(node.iter, node)
            if assign:
                raise ImmutableViolation("Cannot modify array during iteration", assign)

        if node.iter.get("value.id") == "self":
            # check if iterated value may be modified by function calls inside the loop
            iter_name = node.iter.attr
            for call_node in node.get_descendants(vy_ast.Call, {"func.value.id": "self"}):
                fn_name = call_node.func.attr

                fn_node = self.vyper_module.get_children(vy_ast.FunctionDef, {"name": fn_name})[0]
                if _check_iterator_assign(node.iter, fn_node):
                    # check for direct modification
                    raise ImmutableViolation(
                        f"Cannot call '{fn_name}' inside for loop, it potentially "
                        f"modifies iterated storage variable '{iter_name}'",
                        call_node,
                    )

                for name in self.namespace["self"].members[fn_name].recursive_calls:
                    # check for indirect modification
                    fn_node = self.vyper_module.get_children(vy_ast.FunctionDef, {"name": name})[0]
                    if _check_iterator_assign(node.iter, fn_node):
                        raise ImmutableViolation(
                            f"Cannot call '{fn_name}' inside for loop, it may call to '{name}' "
                            f"which potentially modifies iterated storage variable '{iter_name}'",
                            call_node,
                        )

        for_loop_exceptions = []
        iter_name = node.target.id
        for type_ in type_list:
            # type check the for loop body using each possible type for iterator value
            type_ = copy.deepcopy(type_)
            type_.is_immutable = True

            with self.namespace.enter_scope():
                try:
                    self.namespace[iter_name] = type_
                except VyperException as exc:
                    raise exc.with_annotation(node) from None

                try:
                    for n in node.body:
                        self.visit(n)
                    # attach type information to allow non `int128` types in `vyper.parser.stmt`
                    # this is a temporary solution until `vyper.parser` has been refactored
                    node.target._type = type_._id
                    return
                except (TypeMismatch, InvalidOperation) as exc:
                    for_loop_exceptions.append(exc)

        if len(set(str(i) for i in for_loop_exceptions)) == 1:
            # if every attempt at type checking raised the same exception
            raise for_loop_exceptions[0]

        # return an aggregate TypeMismatch that shows all possible exceptions
        # depending on which type is used
        types_str = [str(i) for i in type_list]
        given_str = f"{', '.join(types_str[:1])} or {types_str[-1]}"
        raise TypeMismatch(
            f"Iterator value '{iter_name}' may be cast as {given_str}, "
            "but type checking fails with all possible types:",
            node,
            *(
                (f"Casting '{iter_name}' as {type_}: {exc.message}", exc.annotations[0])
                for type_, exc in zip(type_list, for_loop_exceptions)
            ),
        )
Ejemplo n.º 20
0
    def add_globals_and_events(self, item):
        item_attributes = {"public": False}

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

        # Handle events.
        if not (isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event"):
            item_name, item_attributes = self.get_item_name_and_attributes(item, item_attributes)
            if not all([attr in valid_global_keywords for attr in item_attributes.keys()]):
                raise StructureException('Invalid global keyword used: %s' % item_attributes, item)

        if item.value is not None:
            raise StructureException('May not assign value whilst defining type', item)
        elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event":
            if self._globals or len(self._defs):
                raise EventDeclarationException("Events must all come before global declarations and function definitions", item)
            self._events.append(item)
        elif not isinstance(item.target, ast.Name):
            raise StructureException("Can only assign type to variable in top-level statement", item)

        # Is this a custom unit definition.
        elif item.target.id == 'units':
            if not self._custom_units:
                if not isinstance(item.annotation, ast.Dict):
                    raise VariableDeclarationException("Define custom units using units: { }.", item.target)
                for key, value in zip(item.annotation.keys, item.annotation.values):
                    if not isinstance(value, ast.Str):
                        raise VariableDeclarationException("Custom unit description must be a valid string", value)
                    if not isinstance(key, ast.Name):
                        raise VariableDeclarationException("Custom unit name must be a valid string", key)
                    if key.id in self._custom_units:
                        raise VariableDeclarationException("Custom unit name may only be used once", key)
                    if not is_varname_valid(key.id, custom_units=self._custom_units, custom_structs=self._structs):
                        raise VariableDeclarationException("Custom unit may not be a reserved keyword", key)
                    self._custom_units.add(key.id)
                    self._custom_units_descriptions[key.id] = value.s
            else:
                raise VariableDeclarationException("Custom units can only be defined once", item.target)

        # Check if variable name is valid.
        elif not self.is_valid_varname(item.target.id, item):
            pass

        elif len(self._defs):
            raise StructureException("Global variables must all come before function definitions", item)
        # If the type declaration is of the form public(<type here>), then proceed with
        # the underlying type but also add getters
        elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "address":
            if item.annotation.args[0].id not in premade_contracts:
                raise VariableDeclarationException("Unsupported premade contract declaration", item.annotation.args[0])
            premade_contract = premade_contracts[item.annotation.args[0].id]
            self._contracts[item.target.id] = self.make_contract(premade_contract.body)
            self._globals[item.target.id] = VariableRecord(item.target.id, len(self._globals), BaseType('address'), True)

        elif item_name in self._contracts:
            self._globals[item.target.id] = ContractRecord(item.target.id, len(self._globals), ContractType(item_name), True)
            if item_attributes["public"]:
                typ = ContractType(item_name)
                for getter in self.mk_getter(item.target.id, typ):
                    self._getters.append(self.parse_line('\n' * (item.lineno - 1) + getter))
                    self._getters[-1].pos = getpos(item)

        elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public":
            if isinstance(item.annotation.args[0], ast.Name) and item_name in self._contracts:
                typ = ContractType(item_name)
            else:
                typ = parse_type(item.annotation.args[0], 'storage', custom_units=self._custom_units, custom_structs=self._structs)
            self._globals[item.target.id] = VariableRecord(item.target.id, len(self._globals), typ, True)
            # Adding getters here
            for getter in self.mk_getter(item.target.id, typ):
                self._getters.append(self.parse_line('\n' * (item.lineno - 1) + getter))
                self._getters[-1].pos = getpos(item)

        else:
            self._globals[item.target.id] = VariableRecord(
                item.target.id, len(self._globals),
                parse_type(item.annotation, 'storage', custom_units=self._custom_units, custom_structs=self._structs),
                True
            )
Ejemplo n.º 21
0
    def parse_for(self):
        from .parser import (
            parse_body, )
        # Type 0 for, e.g. for i in list(): ...
        if self._is_list_iter():
            return self.parse_for_list()

        if not isinstance(self.stmt.iter, ast.Call) or \
            not isinstance(self.stmt.iter.func, ast.Name) or \
                not isinstance(self.stmt.target, ast.Name) or \
                    self.stmt.iter.func.id != "range" or \
                        len(self.stmt.iter.args) not in (1, 2):
            raise StructureException(
                "For statements must be of the form `for i in range(rounds): ..` or `for i in range(start, start + rounds): ..`",
                self.stmt.iter)  # noqa

        block_scope_id = id(self.stmt.orelse)
        self.context.start_blockscope(block_scope_id)
        # Type 1 for, e.g. for i in range(10): ...
        if len(self.stmt.iter.args) == 1:
            if not isinstance(self.stmt.iter.args[0], ast.Num):
                raise StructureException("Range only accepts literal values",
                                         self.stmt.iter)
            start = LLLnode.from_list(0, typ='int128', pos=getpos(self.stmt))
            rounds = self.stmt.iter.args[0].n
        elif isinstance(self.stmt.iter.args[0], ast.Num) and isinstance(
                self.stmt.iter.args[1], ast.Num):
            # Type 2 for, e.g. for i in range(100, 110): ...
            start = LLLnode.from_list(self.stmt.iter.args[0].n,
                                      typ='int128',
                                      pos=getpos(self.stmt))
            rounds = LLLnode.from_list(self.stmt.iter.args[1].n -
                                       self.stmt.iter.args[0].n,
                                       typ='int128',
                                       pos=getpos(self.stmt))
        else:
            # Type 3 for, e.g. for i in range(x, x + 10): ...
            if not isinstance(self.stmt.iter.args[1],
                              ast.BinOp) or not isinstance(
                                  self.stmt.iter.args[1].op, ast.Add):
                raise StructureException(
                    "Two-arg for statements must be of the form `for i in range(start, start + rounds): ...`",
                    self.stmt.iter.args[1])
            if ast.dump(self.stmt.iter.args[0]) != ast.dump(
                    self.stmt.iter.args[1].left):
                raise StructureException(
                    "Two-arg for statements of the form `for i in range(x, x + y): ...` must have x identical in both places: %r %r"
                    % (ast.dump(self.stmt.iter.args[0]),
                       ast.dump(self.stmt.iter.args[1].left)), self.stmt.iter)
            if not isinstance(self.stmt.iter.args[1].right, ast.Num):
                raise StructureException("Range only accepts literal values",
                                         self.stmt.iter.args[1])
            start = Expr.parse_value_expr(self.stmt.iter.args[0], self.context)
            rounds = self.stmt.iter.args[1].right.n
        varname = self.stmt.target.id
        pos = self.context.new_variable(varname, BaseType('int128'))
        self.context.forvars[varname] = True
        o = LLLnode.from_list([
            'repeat', pos, start, rounds,
            parse_body(self.stmt.body, self.context)
        ],
                              typ=None,
                              pos=getpos(self.stmt))
        del self.context.vars[varname]
        del self.context.forvars[varname]
        self.context.end_blockscope(block_scope_id)
        return o
Ejemplo n.º 22
0
Archivo: expr.py Proyecto: zutobg/vyper
    def compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.comparators[0], self.context)

        if isinstance(left.typ, ByteArrayType) and isinstance(right.typ, ByteArrayType):
            if left.typ.maxlen != right.typ.maxlen:
                raise TypeMismatchException('Can only compare bytes of the same length', self.expr)
            if left.typ.maxlen > 32 or right.typ.maxlen > 32:
                raise ParserException('Can only compare bytes of length shorter than 32 bytes', self.expr)
        elif isinstance(self.expr.ops[0], ast.In) and \
           isinstance(right.typ, ListType):
            if not are_units_compatible(left.typ, right.typ.subtype) and not are_units_compatible(right.typ.subtype, left.typ):
                raise TypeMismatchException("Can't use IN comparison with different types!", self.expr)
            return self.build_in_comparator()
        else:
            if not are_units_compatible(left.typ, right.typ) and not are_units_compatible(right.typ, left.typ):
                raise TypeMismatchException("Can't compare values with different units!", self.expr)

        if len(self.expr.ops) != 1:
            raise StructureException("Cannot have a comparison with more than two elements", self.expr)
        if isinstance(self.expr.ops[0], ast.Gt):
            op = 'sgt'
        elif isinstance(self.expr.ops[0], ast.GtE):
            op = 'sge'
        elif isinstance(self.expr.ops[0], ast.LtE):
            op = 'sle'
        elif isinstance(self.expr.ops[0], ast.Lt):
            op = 'slt'
        elif isinstance(self.expr.ops[0], ast.Eq):
            op = 'eq'
        elif isinstance(self.expr.ops[0], ast.NotEq):
            op = 'ne'
        else:
            raise Exception("Unsupported comparison operator")

        # Compare (limited to 32) byte arrays.
        if isinstance(left.typ, ByteArrayType) and isinstance(left.typ, ByteArrayType):
            left = Expr(self.expr.left, self.context).lll_node
            right = Expr(self.expr.comparators[0], self.context).lll_node

            def load_bytearray(side):
                if side.location == 'memory':
                    return ['mload', ['add', 32, side]]
                elif side.location == 'storage':
                    return ['sload', ['add', 1, ['sha3_32', side]]]

            return LLLnode.from_list(
                [op, load_bytearray(left), load_bytearray(right)], typ='bool', pos=getpos(self.expr))

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ('eq', 'ne'):
                raise TypeMismatchException("Invalid type for comparison op", self.expr)
        left_type, right_type = left.typ.typ, right.typ.typ

        # Special Case: comparison of a literal integer. If in valid range allow it to be compared.
        if {left_type, right_type} == {'int128', 'uint256'} and {left.typ.is_literal, right.typ.is_literal} == {True, False}:

            comparison_allowed = False
            if left.typ.is_literal and SizeLimits.in_bounds(right_type, left.value):
                comparison_allowed = True
            elif right.typ.is_literal and SizeLimits.in_bounds(left_type, right.value):
                comparison_allowed = True
            op = self._signed_to_unsigned_comparision_op(op)

            if comparison_allowed:
                return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))

        elif {left_type, right_type} == {'uint256', 'uint256'}:
            op = self._signed_to_unsigned_comparision_op(op)
        elif (left_type in ('decimal', 'int128') or right_type in ('decimal', 'int128')) and left_type != right_type:
            raise TypeMismatchException(
                'Implicit conversion from {} to {} disallowed, please convert.'.format(left_type, right_type),
                self.expr
            )

        if left_type == right_type:
            return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))
        else:
            raise TypeMismatchException("Unsupported types for comparison: %r %r" % (left_type, right_type), self.expr)
Ejemplo n.º 23
0
    def parse_return(self):
        from .parser import (make_setter)
        if self.context.return_type is None:
            if self.stmt.value:
                raise TypeMismatchException("Not expecting to return a value",
                                            self.stmt)
            return LLLnode.from_list(['return', 0, 0],
                                     typ=None,
                                     pos=getpos(self.stmt))
        if not self.stmt.value:
            raise TypeMismatchException("Expecting to return a value",
                                        self.stmt)

        sub = Expr(self.stmt.value, self.context).lll_node
        self.context.increment_return_counter()
        # Returning a value (most common case)
        if isinstance(sub.typ, BaseType):
            if not isinstance(self.context.return_type, BaseType):
                raise TypeMismatchException(
                    "Trying to return base type %r, output expecting %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
            sub = unwrap_location(sub)
            if not are_units_compatible(sub.typ, self.context.return_type):
                raise TypeMismatchException(
                    "Return type units mismatch %r %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
            elif is_base_type(sub.typ, self.context.return_type.typ) or \
                    (is_base_type(sub.typ, 'int128') and is_base_type(self.context.return_type, 'int256')):
                return LLLnode.from_list(
                    ['seq', ['mstore', 0, sub], ['return', 0, 32]],
                    typ=None,
                    pos=getpos(self.stmt))
            if sub.typ.is_literal and SizeLimits.in_bounds(
                    self.context.return_type.typ, sub.value):
                return LLLnode.from_list(
                    ['seq', ['mstore', 0, sub], ['return', 0, 32]],
                    typ=None,
                    pos=getpos(self.stmt))
            else:
                raise TypeMismatchException(
                    "Unsupported type conversion: %r to %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
        # Returning a byte array
        elif isinstance(sub.typ, ByteArrayType):
            if not isinstance(self.context.return_type, ByteArrayType):
                raise TypeMismatchException(
                    "Trying to return base type %r, output expecting %r" %
                    (sub.typ, self.context.return_type), self.stmt.value)
            if sub.typ.maxlen > self.context.return_type.maxlen:
                raise TypeMismatchException(
                    "Cannot cast from greater max-length %d to shorter max-length %d"
                    % (sub.typ.maxlen, self.context.return_type.maxlen),
                    self.stmt.value)

            zero_padder = LLLnode.from_list(['pass'])
            if sub.typ.maxlen > 0:
                zero_pad_i = self.context.new_placeholder(
                    BaseType('uint256'))  # Iterator used to zero pad memory.
                zero_padder = LLLnode.from_list(
                    [
                        'repeat',
                        zero_pad_i,
                        ['mload', '_loc'],
                        sub.typ.maxlen,
                        [
                            'seq',
                            [
                                'if',
                                ['gt', ['mload', zero_pad_i], sub.typ.maxlen],
                                'break'
                            ],  # stay within allocated bounds
                            [
                                'mstore8',
                                [
                                    'add', ['add', 32, '_loc'],
                                    ['mload', zero_pad_i]
                                ], 0
                            ]
                        ]
                    ],
                    annotation="Zero pad")

            # Returning something already in memory
            if sub.location == 'memory':
                return LLLnode.from_list([
                    'with', '_loc', sub,
                    [
                        'seq', ['mstore', ['sub', '_loc', 32], 32],
                        zero_padder,
                        [
                            'return', ['sub', '_loc', 32],
                            ['ceil32', ['add', ['mload', '_loc'], 64]]
                        ]
                    ]
                ],
                                         typ=None,
                                         pos=getpos(self.stmt))

            # Copying from storage
            elif sub.location == 'storage':
                # Instantiate a byte array at some index
                fake_byte_array = LLLnode(self.context.get_next_mem() + 32,
                                          typ=sub.typ,
                                          location='memory',
                                          pos=getpos(self.stmt))
                o = [
                    'with',
                    '_loc',
                    self.context.get_next_mem() + 32,
                    [
                        'seq',
                        # Copy the data to this byte array
                        make_byte_array_copier(fake_byte_array, sub),
                        # Store the number 32 before it for ABI formatting purposes
                        ['mstore', self.context.get_next_mem(), 32],
                        zero_padder,
                        # Return it
                        [
                            'return',
                            self.context.get_next_mem(),
                            [
                                'add',
                                [
                                    'ceil32',
                                    [
                                        'mload',
                                        self.context.get_next_mem() + 32
                                    ]
                                ], 64
                            ]
                        ]
                    ]
                ]
                return LLLnode.from_list(o, typ=None, pos=getpos(self.stmt))
            else:
                raise Exception("Invalid location: %s" % sub.location)

        elif isinstance(sub.typ, ListType):
            sub_base_type = re.split(r'\(|\[', str(sub.typ.subtype))[0]
            ret_base_type = re.split(r'\(|\[',
                                     str(self.context.return_type.subtype))[0]
            if sub_base_type != ret_base_type:
                raise TypeMismatchException(
                    "List return type %r does not match specified return type, expecting %r"
                    % (sub_base_type, ret_base_type), self.stmt)
            elif sub.location == "memory" and sub.value != "multi":
                return LLLnode.from_list([
                    'return', sub,
                    get_size_of_type(self.context.return_type) * 32
                ],
                                         typ=None,
                                         pos=getpos(self.stmt))
            else:
                new_sub = LLLnode.from_list(self.context.new_placeholder(
                    self.context.return_type),
                                            typ=self.context.return_type,
                                            location='memory')
                setter = make_setter(new_sub,
                                     sub,
                                     'memory',
                                     pos=getpos(self.stmt))
                return LLLnode.from_list([
                    'seq', setter,
                    [
                        'return', new_sub,
                        get_size_of_type(self.context.return_type) * 32
                    ]
                ],
                                         typ=None,
                                         pos=getpos(self.stmt))

        # Returning a tuple.
        elif isinstance(sub.typ, TupleType):
            if len(self.context.return_type.members) != len(sub.typ.members):
                raise StructureException("Tuple lengths don't match!",
                                         self.stmt)

            subs = []
            dynamic_offset_counter = LLLnode(
                self.context.get_next_mem(),
                typ=None,
                annotation="dynamic_offset_counter"
            )  # dynamic offset position counter.
            new_sub = LLLnode.from_list(self.context.get_next_mem() + 32,
                                        typ=self.context.return_type,
                                        location='memory',
                                        annotation='new_sub')
            keyz = list(range(len(sub.typ.members)))
            dynamic_offset_start = 32 * len(
                sub.args)  # The static list of args end.
            left_token = LLLnode.from_list('_loc',
                                           typ=new_sub.typ,
                                           location="memory")

            def get_dynamic_offset_value():
                # Get value of dynamic offset counter.
                return ['mload', dynamic_offset_counter]

            def increment_dynamic_offset(dynamic_spot):
                # Increment dyanmic offset counter in memory.
                return [
                    'mstore', dynamic_offset_counter,
                    [
                        'add',
                        ['add', ['ceil32', ['mload', dynamic_spot]], 32],
                        ['mload', dynamic_offset_counter]
                    ]
                ]

            for i, typ in enumerate(keyz):
                arg = sub.args[i]
                variable_offset = LLLnode.from_list(
                    ['add', 32 * i, left_token],
                    typ=arg.typ,
                    annotation='variable_offset')
                if isinstance(arg.typ, ByteArrayType):
                    # Store offset pointer value.
                    subs.append([
                        'mstore', variable_offset,
                        get_dynamic_offset_value()
                    ])

                    # Store dynamic data, from offset pointer onwards.
                    dynamic_spot = LLLnode.from_list(
                        ['add', left_token,
                         get_dynamic_offset_value()],
                        location="memory",
                        typ=arg.typ,
                        annotation='dynamic_spot')
                    subs.append(
                        make_setter(dynamic_spot,
                                    arg,
                                    location="memory",
                                    pos=getpos(self.stmt)))
                    subs.append(increment_dynamic_offset(dynamic_spot))

                elif isinstance(arg.typ, BaseType):
                    subs.append(
                        make_setter(variable_offset,
                                    arg,
                                    "memory",
                                    pos=getpos(self.stmt)))
                else:
                    raise Exception("Can't return type %s as part of tuple",
                                    type(arg.typ))

            setter = LLLnode.from_list([
                'seq', [
                    'mstore', dynamic_offset_counter, dynamic_offset_start
                ], ['with', '_loc', new_sub, ['seq'] + subs]
            ],
                                       typ=None)

            return LLLnode.from_list([
                'seq', setter, ['return', new_sub,
                                get_dynamic_offset_value()]
            ],
                                     typ=None,
                                     pos=getpos(self.stmt))
        else:
            raise TypeMismatchException("Can only return base type!",
                                        self.stmt)
Ejemplo n.º 24
0
Archivo: expr.py Proyecto: zutobg/vyper
    def call(self):
        from .parser import (
            external_contract_call,
            pack_arguments,
        )
        from vyper.functions import (
            dispatch_table,
        )

        if isinstance(self.expr.func, ast.Name):
            function_name = self.expr.func.id
            if function_name in dispatch_table:
                return dispatch_table[function_name](self.expr, self.context)
            else:
                err_msg = "Not a top-level function: {}".format(function_name)
                if function_name in [x.split('(')[0] for x, _ in self.context.sigs['self'].items()]:
                    err_msg += ". Did you mean self.{}?".format(function_name)
                raise StructureException(err_msg, self.expr)
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Name) and self.expr.func.value.id == "self":
            expr_args = [Expr(arg, self.context).lll_node for arg in self.expr.args]
            method_name = self.expr.func.attr
            sig = FunctionSignature.lookup_sig(self.context.sigs, method_name, expr_args, self.expr, self.context)
            if self.context.is_constant and not sig.const:
                raise ConstancyViolationException(
                    "May not call non-constant function '%s' within a constant function." % (method_name),
                    getpos(self.expr)
                )
            add_gas = sig.gas  # gas of call
            inargs, inargsize = pack_arguments(sig, expr_args, self.context, pos=getpos(self.expr))
            output_placeholder = self.context.new_placeholder(typ=sig.output_type)
            multi_arg = []
            if isinstance(sig.output_type, BaseType):
                returner = output_placeholder
            elif isinstance(sig.output_type, ByteArrayType):
                returner = output_placeholder + 32
            elif isinstance(sig.output_type, TupleType):
                returner = output_placeholder
            else:
                raise TypeMismatchException("Invalid output type: %r" % sig.output_type, self.expr)

            o = LLLnode.from_list(multi_arg +
                    ['seq',
                        ['assert', ['call', ['gas'], ['address'], 0,
                                        inargs, inargsize,
                                        output_placeholder, get_size_of_type(sig.output_type) * 32]], returner],
                typ=sig.output_type, location='memory',
                pos=getpos(self.expr), add_gas_estimate=add_gas, annotation='Internal Call: %s' % method_name)
            o.gas += sig.gas
            return o
        elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Call):
            contract_name = self.expr.func.value.func.id
            contract_address = Expr.parse_value_expr(self.expr.func.value.args[0], self.context)
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas)
        elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.sigs:
            contract_name = self.expr.func.value.attr
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas)
        elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.globals:
            contract_name = self.context.globals[self.expr.func.value.attr].typ.unit
            var = self.context.globals[self.expr.func.value.attr]
            contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr))
            value, gas = self._get_external_contract_keywords()
            return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas)
        else:
            raise StructureException("Unsupported operator: %r" % ast.dump(self.expr), self.expr)
Ejemplo n.º 25
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)
Ejemplo n.º 26
0
def _sha3(expr, args, kwargs, context):
    raise StructureException(
        "sha3 function has been deprecated in favor of keccak256")
Ejemplo n.º 27
0
def concat(expr, context):
    args = [Expr(arg, context).lll_node for arg in expr.args]
    if len(args) < 2:
        raise StructureException("Concat expects at least two arguments", expr)

    prev_type = ''
    for i, (expr_arg, arg) in enumerate(zip(expr.args, args)):
        if not isinstance(arg.typ, ByteArrayLike) and not is_base_type(arg.typ, 'bytes32'):
            raise TypeMismatchException("Concat expects string, bytes or bytes32 objects", expr_arg)

        current_type = 'bytes' if isinstance(arg.typ, ByteArrayType) or is_base_type(arg.typ, 'bytes32') else 'string'
        if prev_type and current_type != prev_type:
            raise TypeMismatchException("Concat expects consistant use of string or byte types, user either bytes or string.", expr_arg)
        prev_type = current_type

    if current_type == 'string':
        ReturnType = StringType
    else:
        ReturnType = ByteArrayType

    # Maximum length of the output
    total_maxlen = sum([arg.typ.maxlen if isinstance(arg.typ, ByteArrayType) else 32 for arg in args])
    # Node representing the position of the output in memory
    placeholder = context.new_placeholder(ReturnType(total_maxlen))
    # Object representing the output
    seq = []
    # For each argument we are concatenating...
    for arg in args:
        # Start pasting into a position the starts at zero, and keeps
        # incrementing as we concatenate arguments
        placeholder_node = LLLnode.from_list(['add', placeholder, '_poz'], typ=ReturnType(total_maxlen), location='memory')
        placeholder_node_plus_32 = LLLnode.from_list(['add', ['add', placeholder, '_poz'], 32], typ=ReturnType(total_maxlen), location='memory')
        if isinstance(arg.typ, ReturnType):
            # Ignore empty strings
            if arg.typ.maxlen == 0:
                continue
            # Get the length of the current argument
            if arg.location == "memory":
                length = LLLnode.from_list(['mload', '_arg'], typ=BaseType('int128'))
                argstart = LLLnode.from_list(['add', '_arg', 32], typ=arg.typ, location=arg.location)
            elif arg.location == "storage":
                length = LLLnode.from_list(['sload', ['sha3_32', '_arg']], typ=BaseType('int128'))
                argstart = LLLnode.from_list(['add', ['sha3_32', '_arg'], 1], typ=arg.typ, location=arg.location)
            # Make a copier to copy over data from that argument
            seq.append(['with', '_arg', arg,
                            ['seq',
                                make_byte_slice_copier(placeholder_node_plus_32,
                                                       argstart,
                                                       length,
                                                       arg.typ.maxlen, pos=getpos(expr)),
                                # Change the position to start at the correct
                                # place to paste the next value
                                ['set', '_poz', ['add', '_poz', length]]]])
        else:
            seq.append(['seq',
                            ['mstore', ['add', placeholder_node, 32], unwrap_location(arg)],
                            ['set', '_poz', ['add', '_poz', 32]]])
    # The position, after all arguments are processing, equals the total
    # length. Paste this in to make the output a proper bytearray
    seq.append(['mstore', placeholder, '_poz'])
    # Memory location of the output
    seq.append(placeholder)
    return LLLnode.from_list(
        ['with', '_poz', 0, ['seq'] + seq], typ=ReturnType(total_maxlen), location='memory', pos=getpos(expr), annotation='concat'
    )
Ejemplo n.º 28
0
 def parse_Name(self):
     if self.stmt.id == "vdb":
         return IRnode("debugger")
     else:
         raise StructureException(
             f"Unsupported statement type: {type(self.stmt)}", self.stmt)
Ejemplo n.º 29
0
def make_external_call(stmt_expr, context):
    from vyper.parser.expr import Expr
    value, gas = get_external_contract_keywords(stmt_expr, context)

    if isinstance(stmt_expr.func, ast.Attribute) and isinstance(
            stmt_expr.func.value, ast.Call):
        contract_name = stmt_expr.func.value.func.id
        contract_address = Expr.parse_value_expr(stmt_expr.func.value.args[0],
                                                 context)

        return external_contract_call(
            stmt_expr,
            context,
            contract_name,
            contract_address,
            pos=getpos(stmt_expr),
            value=value,
            gas=gas,
        )

    elif isinstance(
            stmt_expr.func.value, ast.Attribute
    ) and stmt_expr.func.value.attr in context.sigs:  # noqa: E501
        contract_name = stmt_expr.func.value.attr
        var = context.globals[stmt_expr.func.value.attr]
        contract_address = unwrap_location(
            LLLnode.from_list(
                var.pos,
                typ=var.typ,
                location='storage',
                pos=getpos(stmt_expr),
                annotation='self.' + stmt_expr.func.value.attr,
            ))

        return external_contract_call(
            stmt_expr,
            context,
            contract_name,
            contract_address,
            pos=getpos(stmt_expr),
            value=value,
            gas=gas,
        )

    elif isinstance(
            stmt_expr.func.value, ast.Attribute
    ) and stmt_expr.func.value.attr in context.globals:  # noqa: E501
        contract_name = context.globals[stmt_expr.func.value.attr].typ.unit
        var = context.globals[stmt_expr.func.value.attr]
        contract_address = unwrap_location(
            LLLnode.from_list(
                var.pos,
                typ=var.typ,
                location='storage',
                pos=getpos(stmt_expr),
                annotation='self.' + stmt_expr.func.value.attr,
            ))

        return external_contract_call(
            stmt_expr,
            context,
            contract_name,
            contract_address,
            pos=getpos(stmt_expr),
            value=value,
            gas=gas,
        )

    else:
        raise StructureException("Unsupported operator.", stmt_expr)
Ejemplo n.º 30
0
def call_self_private(stmt_expr, context, sig):
    # ** Private Call **
    # Steps:
    # (x) push current local variables
    # (x) push arguments
    # (x) push jumpdest (callback ptr)
    # (x) jump to label
    # (x) pop return values
    # (x) pop local variables

    method_name, expr_args, sig = call_lookup_specs(stmt_expr, context)
    pre_init = []
    pop_local_vars = []
    push_local_vars = []
    pop_return_values = []
    push_args = []

    # Push local variables.
    var_slots = [
        (v.pos, v.size) for name, v in context.vars.items()
        if v.location == 'memory'
    ]
    if var_slots:
        var_slots.sort(key=lambda x: x[0])
        mem_from, mem_to = var_slots[0][0], var_slots[-1][0] + var_slots[-1][1] * 32

        i_placeholder = context.new_placeholder(BaseType('uint256'))
        local_save_ident = f"_{stmt_expr.lineno}_{stmt_expr.col_offset}"
        push_loop_label = 'save_locals_start' + local_save_ident
        pop_loop_label = 'restore_locals_start' + local_save_ident

        if mem_to - mem_from > 320:
            push_local_vars = [
                    ['mstore', i_placeholder, mem_from],
                    ['label', push_loop_label],
                    ['mload', ['mload', i_placeholder]],
                    ['mstore', i_placeholder, ['add', ['mload', i_placeholder], 32]],
                    ['if', ['lt', ['mload', i_placeholder], mem_to],
                        ['goto', push_loop_label]]
            ]
            pop_local_vars = [
                ['mstore', i_placeholder, mem_to - 32],
                ['label', pop_loop_label],
                ['mstore', ['mload', i_placeholder], 'pass'],
                ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],
                ['if', ['ge', ['mload', i_placeholder], mem_from],
                       ['goto', pop_loop_label]]
            ]
        else:
            push_local_vars = [['mload', pos] for pos in range(mem_from, mem_to, 32)]
            pop_local_vars = [['mstore', pos, 'pass'] for pos in range(mem_to-32, mem_from-32, -32)]

    # Push Arguments
    if expr_args:
        inargs, inargsize, arg_pos = pack_arguments(
            sig,
            expr_args,
            context,
            stmt_expr,
            return_placeholder=False,
        )
        push_args += [inargs]  # copy arguments first, to not mess up the push/pop sequencing.

        static_arg_size = 32 * sum(
                [get_static_size_of_type(arg.typ)
                    for arg in expr_args])
        static_pos = int(arg_pos + static_arg_size)
        needs_dyn_section = any(
                [has_dynamic_data(arg.typ)
                    for arg in expr_args])

        if needs_dyn_section:
            ident = f'push_args_{sig.method_id}_{stmt_expr.lineno}_{stmt_expr.col_offset}'
            start_label = ident + '_start'
            end_label = ident + '_end'
            i_placeholder = context.new_placeholder(BaseType('uint256'))

            # Calculate copy start position.
            # Given | static | dynamic | section in memory,
            # copy backwards so the values are in order on the stack.
            # We calculate i, the end of the whole encoded part
            # (i.e. the starting index for copy)
            # by taking ceil32(len<arg>) + offset<arg> + arg_pos
            # for the last dynamic argument and arg_pos is the start
            # the whole argument section.
            idx = 0
            for arg in expr_args:
                if isinstance(arg.typ, ByteArrayLike):
                    last_idx = idx
                idx += get_static_size_of_type(arg.typ)
            push_args += [
                ['with', 'offset', ['mload', arg_pos + last_idx * 32],
                    ['with', 'len_pos', ['add', arg_pos, 'offset'],
                        ['with', 'len_value', ['mload', 'len_pos'],
                            ['mstore', i_placeholder,
                                ['add', 'len_pos', ['ceil32', 'len_value']]]]]]
            ]
            # loop from end of dynamic section to start of dynamic section,
            # pushing each element onto the stack.
            push_args += [

                ['label', start_label],
                ['if', ['lt', ['mload', i_placeholder], static_pos],
                    ['goto', end_label]],
                ['mload', ['mload', i_placeholder]],
                ['mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32]],  # decrease i
                ['goto', start_label],
                ['label', end_label]
            ]

        # push static section
        push_args += [
            ['mload', pos] for pos in reversed(range(arg_pos, static_pos, 32))
        ]
    elif sig.args:
        raise StructureException(
            f"Wrong number of args for: {sig.name} (0 args given, expected {len(sig.args)})",
            stmt_expr
        )

    # Jump to function label.
    jump_to_func = [
        ['add', ['pc'], 6],  # set callback pointer.
        ['goto', f'priv_{sig.method_id}'],
        ['jumpdest'],
    ]

    # Pop return values.
    returner = [0]
    if sig.output_type:
        output_placeholder, returner, output_size = call_make_placeholder(stmt_expr, context, sig)
        if output_size > 0:
            dynamic_offsets = []
            if isinstance(sig.output_type, (BaseType, ListType)):
                pop_return_values = [
                    ['mstore', ['add', output_placeholder, pos], 'pass']
                    for pos in range(0, output_size, 32)
                ]
            elif isinstance(sig.output_type, ByteArrayLike):
                dynamic_offsets = [(0, sig.output_type)]
                pop_return_values = [
                    ['pop', 'pass'],
                ]
            elif isinstance(sig.output_type, TupleLike):
                static_offset = 0
                pop_return_values = []
                for out_type in sig.output_type.members:
                    if isinstance(out_type, ByteArrayLike):
                        pop_return_values.append(
                            ['mstore', ['add', output_placeholder, static_offset], 'pass']
                        )
                        dynamic_offsets.append(
                            (['mload', ['add', output_placeholder, static_offset]], out_type)
                        )
                    else:
                        pop_return_values.append(
                            ['mstore', ['add', output_placeholder, static_offset], 'pass']
                        )
                    static_offset += 32

            # append dynamic unpacker.
            dyn_idx = 0
            for in_memory_offset, _out_type in dynamic_offsets:
                ident = f"{stmt_expr.lineno}_{stmt_expr.col_offset}_arg_{dyn_idx}"
                dyn_idx += 1
                start_label = 'dyn_unpack_start_' + ident
                end_label = 'dyn_unpack_end_' + ident
                i_placeholder = context.new_placeholder(typ=BaseType('uint256'))
                begin_pos = ['add', output_placeholder, in_memory_offset]
                # loop until length.
                o = LLLnode.from_list(
                    ['seq_unchecked',
                        ['mstore', begin_pos, 'pass'],  # get len
                        ['mstore', i_placeholder, 0],
                        ['label', start_label],
                        [  # break
                            'if',
                            ['ge', ['mload', i_placeholder], ['ceil32', ['mload', begin_pos]]],
                            ['goto', end_label]
                        ],
                        [  # pop into correct memory slot.
                            'mstore',
                            ['add', ['add', begin_pos, 32], ['mload', i_placeholder]],
                            'pass',
                        ],
                        # increment i
                        ['mstore', i_placeholder, ['add', 32, ['mload', i_placeholder]]],
                        ['goto', start_label],
                        ['label', end_label]],
                    typ=None, annotation='dynamic unpacker', pos=getpos(stmt_expr))
                pop_return_values.append(o)

    call_body = list(itertools.chain(
        ['seq_unchecked'],
        pre_init,
        push_local_vars,
        push_args,
        jump_to_func,
        pop_return_values,
        pop_local_vars,
        [returner],
    ))
    # If we have no return, we need to pop off
    pop_returner_call_body = ['pop', call_body] if sig.output_type is None else call_body

    o = LLLnode.from_list(
        pop_returner_call_body,
        typ=sig.output_type,
        location='memory',
        pos=getpos(stmt_expr),
        annotation=f'Internal Call: {method_name}',
        add_gas_estimate=sig.gas
    )
    o.gas += sig.gas
    return o