Ejemplo n.º 1
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, (ast.Str, ast.Call)):
            expr = Expr(arg, context)
            source_lll = expr.lll_node
            typ = source_lll.typ

            if isinstance(arg, ast.Str):
                if len(arg.s) > typ.maxlen:
                    raise TypeMismatchException(
                        "Data input bytes are to big: %r %r" %
                        (len(arg.s), typ), pos)

            tmp_variable = context.new_variable(
                '_log_pack_var_%i_%i' % (arg.lineno, arg.col_offset),
                source_lll.typ,
            )
            tmp_variable_node = LLLnode.from_list(
                tmp_variable,
                typ=source_lll.typ,
                pos=getpos(arg),
                location="memory",
                annotation='log_prealloacted %r' % 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

    requires_dynamic_offset = any(
        [isinstance(data.typ, ByteArrayLike) for data in expected_data])
    if requires_dynamic_offset:
        # Iterator used to zero pad memory.
        zero_pad_i = context.new_placeholder(BaseType('uint256'))
        dynamic_offset_counter = context.new_placeholder(BaseType(32))
        dynamic_placeholder = context.new_placeholder(BaseType(32))
    else:
        dynamic_offset_counter = None
        zero_pad_i = None

    # Create placeholder for static args. Note: order of new_*() is important.
    placeholder_map = {}
    for i, (_arg, data) in enumerate(zip(args, expected_data)):
        typ = data.typ
        if not isinstance(typ, ByteArrayLike):
            placeholder = context.new_placeholder(typ)
        else:
            placeholder = context.new_placeholder(BaseType(32))
        placeholder_map[i] = placeholder

    # Populate static placeholders.
    for i, (arg, data) in enumerate(zip(args, expected_data)):
        typ = data.typ
        placeholder = placeholder_map[i]
        if not isinstance(typ, ByteArrayLike):
            holder, maxlen = pack_args_by_32(
                holder,
                maxlen,
                prealloacted.get(i, arg),
                typ,
                context,
                placeholder,
                zero_pad_i=zero_pad_i,
                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 = placeholder_map[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):
            pack_args_by_32(holder=holder,
                            maxlen=maxlen,
                            arg=prealloacted.get(i, arg),
                            typ=typ,
                            context=context,
                            placeholder=placeholder_map[i],
                            datamem_start=datamem_start,
                            dynamic_offset_counter=dynamic_offset_counter,
                            zero_pad_i=zero_pad_i,
                            pos=pos)

    return holder, maxlen, dynamic_offset_counter, datamem_start
Ejemplo n.º 2
0
def pack_logging_data(arg_nodes, arg_types, context, pos):
    # Checks to see if there's any data
    if not arg_nodes:
        return ["seq"], 0, None, 0
    holder = ["seq"]
    maxlen = len(arg_nodes) * 32  # total size of all packed args (upper limit)

    # Unroll any function calls, to temp variables.
    prealloacted = {}
    for idx, node in enumerate(arg_nodes):

        if isinstance(
                node,
            (vy_ast.Str, vy_ast.Call)) and node.get("func.id") != "empty":
            expr = Expr(node, context)
            source_lll = expr.lll_node
            tmp_variable = context.new_internal_variable(source_lll.typ)
            tmp_variable_node = LLLnode.from_list(
                tmp_variable,
                typ=source_lll.typ,
                pos=getpos(node),
                location="memory",
                annotation=f"log_prealloacted {source_lll.typ}",
            )
            # Copy bytes.
            holder.append(
                make_setter(tmp_variable_node,
                            source_lll,
                            pos=getpos(node),
                            location="memory"))
            prealloacted[idx] = tmp_variable_node

    # Create internal variables for for dynamic and static args.
    static_types = []
    for typ in arg_types:
        static_types.append(
            typ if not typ.is_dynamic_size else Uint256Definition())

    requires_dynamic_offset = any(typ.is_dynamic_size for typ in arg_types)

    dynamic_offset_counter = None
    if requires_dynamic_offset:
        # TODO refactor out old type objects
        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, (node, typ) in enumerate(zip(arg_nodes, arg_types)):
        placeholder = static_vars[i]
        if not isinstance(typ, ArrayValueAbstractType):
            holder, maxlen = pack_args_by_32(
                holder,
                maxlen,
                prealloacted.get(i, node),
                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 typ in arg_types:
        if typ.is_dynamic_size:
            maxlen += typ.size_in_bytes

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

    # Copy necessary data into allocated dynamic section.
    for i, (node, typ) in enumerate(zip(arg_nodes, arg_types)):
        if isinstance(typ, ArrayValueAbstractType):
            if isinstance(node,
                          vy_ast.Call) and node.func.get("id") == "empty":
                # TODO add support for this
                raise StructureException(
                    "Cannot use `empty` on Bytes or String types within an event log",
                    node)
            pack_args_by_32(
                holder=holder,
                maxlen=maxlen,
                arg=prealloacted.get(i, node),
                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.º 3
0
def parse_private_function(code: ast.FunctionDef, sig: FunctionSignature,
                           context: Context) -> LLLnode:
    """
    Parse a private function (FuncDef), and produce full function body.

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

    validate_private_function(code, sig)

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

    # Create callback_ptr, this stores a destination in the bytecode for a private
    # function to jump to after a function has executed.
    clampers: List[LLLnode] = []

    # Allocate variable space.
    context.memory_allocator.increase_memory(sig.max_copy_size)

    _post_callback_ptr = f"{sig.name}_{sig.method_id}_post_callback_ptr"
    context.callback_ptr = context.new_placeholder(typ=BaseType('uint256'))
    clampers.append(
        LLLnode.from_list(
            ['mstore', context.callback_ptr, 'pass'],
            annotation='pop callback pointer',
        ))
    if sig.total_default_args > 0:
        clampers.append(LLLnode.from_list(['label', _post_callback_ptr]))

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

    # Generate copiers
    if len(sig.base_args) == 0:
        copier = ['pass']
        clampers.append(LLLnode.from_list(copier))
    elif sig.total_default_args == 0:
        copier = get_private_arg_copier(
            total_size=sig.base_copy_size,
            memory_dest=MemoryPositions.RESERVED_MEMORY)
        clampers.append(LLLnode.from_list(copier))

    # Fill variable positions
    for arg in sig.args:
        if isinstance(arg.typ, ByteArrayLike):
            mem_pos, _ = context.memory_allocator.increase_memory(
                32 * get_size_of_type(arg.typ))
            context.vars[arg.name] = VariableRecord(arg.name, mem_pos, arg.typ,
                                                    False)
        else:
            context.vars[arg.name] = VariableRecord(
                arg.name,
                MemoryPositions.RESERVED_MEMORY + arg.pos,
                arg.typ,
                False,
            )

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

        if not unpackers:
            unpackers = ['pass']

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

    # Function has default arguments.
    if sig.total_default_args > 0:  # Function with default parameters.

        default_sigs = sig_utils.generate_default_arg_sigs(
            code, context.sigs, context.global_ctx)
        sig_chain: List[Any] = ['seq']

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

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

            # Load all variables in default section, if private,
            # because the stack is a linear pipe.
            copier_arg_count = len(default_sig.args)
            copier_arg_names = current_sig_arg_names

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

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

                # Copy set default parameters from calldata
                dynamics = []
                for arg_name in copier_arg_names:
                    var = context.vars[arg_name]
                    if isinstance(var.typ, ByteArrayLike):
                        _size = 32
                        dynamics.append(var.pos)
                    else:
                        _size = var.size * 32
                    default_copiers.append(
                        get_private_arg_copier(
                            memory_dest=var.pos,
                            total_size=_size,
                        ))

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

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

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

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

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

    return o
Ejemplo n.º 4
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