Beispiel #1
0
def _call_make_placeholder(stmt_expr, context, sig):
    if sig.output_type is None:
        return 0, 0, 0

    output_placeholder = context.new_internal_variable(typ=sig.output_type)
    output_size = get_size_of_type(sig.output_type) * 32

    if isinstance(sig.output_type, BaseType):
        returner = output_placeholder
    elif isinstance(sig.output_type, ByteArrayLike):
        returner = output_placeholder
    elif isinstance(sig.output_type, TupleLike):
        # incase of struct we need to decode the output and then return it
        returner = ["seq"]
        decoded_placeholder = context.new_internal_variable(
            typ=sig.output_type)
        decoded_node = LLLnode(decoded_placeholder,
                               typ=sig.output_type,
                               location="memory")
        output_node = LLLnode(output_placeholder,
                              typ=sig.output_type,
                              location="memory")
        returner.append(abi_decode(decoded_node, output_node))
        returner.extend([decoded_placeholder])
    elif isinstance(sig.output_type, ListType):
        returner = output_placeholder
    else:
        raise TypeCheckFailure(f"Invalid output type: {sig.output_type}")
    return output_placeholder, returner, output_size
Beispiel #2
0
def abi_decode(lll_node, src, pos=None):
    os = o_list(lll_node, pos=pos)
    lll_ret = ["seq"]
    parent_abi_t = abi_type_of(lll_node.typ)
    for i, o in enumerate(os):
        abi_t = abi_type_of(o.typ)
        src_loc = LLLnode("src_loc", typ=o.typ, location=src.location)
        if parent_abi_t.is_tuple():
            if abi_t.is_dynamic():
                child_loc = ["add", "src", unwrap_location(src_loc)]
                child_loc = LLLnode.from_list(child_loc,
                                              typ=o.typ,
                                              location=src.location)
            else:
                child_loc = src_loc
            # descend into the child tuple
            lll_ret.append(abi_decode(o, child_loc, pos=pos))
        else:
            lll_ret.append(
                make_setter(o, src_loc, location=o.location, pos=pos))

        if i + 1 == len(os):
            pass  # optimize out the last pointer increment
        else:
            sz = abi_t.embedded_static_size()
            lll_ret.append(["set", "src_loc", ["add", "src_loc", sz]])

    lll_ret = ["with", "src", src, ["with", "src_loc", "src", lll_ret]]

    return lll_ret
Beispiel #3
0
def unwrap_location(orig):
    if orig.location == "memory":
        return LLLnode.from_list(["mload", orig], typ=orig.typ)
    elif orig.location == "storage":
        return LLLnode.from_list(["sload", orig], typ=orig.typ)
    elif orig.location == "calldata":
        return LLLnode.from_list(["calldataload", orig], typ=orig.typ)
    else:
        # handle None value inserted by `empty`
        if orig.value is None:
            return LLLnode.from_list(0, typ=orig.typ)
        return orig
Beispiel #4
0
def _make_array_index_setter(target, target_token, pos, location, offset):
    if location == "memory" and isinstance(target.value, int):
        offset = target.value + 32 * get_size_of_type(
            target.typ.subtype) * offset
        return LLLnode.from_list([offset],
                                 typ=target.typ.subtype,
                                 location=location,
                                 pos=pos)
    else:
        return add_variable_offset(
            target_token,
            LLLnode.from_list(offset, typ="int256"),
            pos=pos,
            array_bounds_check=False,
        )
Beispiel #5
0
def lazy_abi_decode(typ, src, pos=None):
    if isinstance(typ, (ListType, TupleLike)):
        if isinstance(typ, TupleLike):
            ts = typ.tuple_members()
        else:
            ts = [typ.subtyp for _ in range(typ.count)]
        ofst = 0
        os = []
        for t in ts:
            child_abi_t = abi_type_of(t)
            loc = _add_ofst(src, ofst)
            if child_abi_t.is_dynamic():
                # load the offset word, which is the
                # (location-independent) offset from the start of the
                # src buffer.
                dyn_ofst = unwrap_location(ofst)
                loc = _add_ofst(src, dyn_ofst)
            os.append(lazy_abi_decode(t, loc, pos))
            ofst += child_abi_t.embedded_static_size()

        return LLLnode.from_list(["multi"] + os, typ=typ, pos=pos)

    elif isinstance(typ, (BaseType, ByteArrayLike)):
        return unwrap_location(src)
    else:
        raise CompilerPanic(f"unknown type for lazy_abi_decode {typ}")
Beispiel #6
0
def mzero(dst, nbytes):
    # calldatacopy from past-the-end gives zero bytes.
    # cf. YP H.2 (ops section) with CALLDATACOPY spec.
    return LLLnode.from_list(
        # calldatacopy mempos calldatapos len
        ["calldatacopy", dst, "calldatasize", nbytes],
        annotation="mzero",
    )
Beispiel #7
0
def byte_array_to_num(
    arg,
    expr,
    out_type,
    offset=32,
):
    if arg.location == "memory":
        lengetter = LLLnode.from_list(["mload", "_sub"],
                                      typ=BaseType("int256"))
        first_el_getter = LLLnode.from_list(["mload", ["add", 32, "_sub"]],
                                            typ=BaseType("int256"))
    elif arg.location == "storage":
        lengetter = LLLnode.from_list(["sload", "_sub"],
                                      typ=BaseType("int256"))
        first_el_getter = LLLnode.from_list(["sload", ["add", 1, "_sub"]],
                                            typ=BaseType("int256"))
    if out_type == "int128":
        result = int128_clamp(
            ["div", "_el1", ["exp", 256, ["sub", 32, "_len"]]])
    elif out_type in ("int256", "uint256"):
        result = ["div", "_el1", ["exp", 256, ["sub", offset, "_len"]]]
    return LLLnode.from_list(
        [
            "with",
            "_sub",
            arg,
            [
                "with",
                "_el1",
                first_el_getter,
                ["with", "_len", ["clamp", 0, lengetter, 32], result],
            ],
        ],
        typ=BaseType(out_type),
        annotation=f"bytearray to number ({out_type})",
    )
Beispiel #8
0
def o_list(lll_node, pos=None):
    lll_t = lll_node.typ
    if isinstance(lll_t, (TupleLike, ListType)):
        if lll_node.value == "multi":  # is literal
            ret = lll_node.args
        else:
            ks = (lll_t.tuple_keys() if isinstance(lll_t, TupleLike) else [
                LLLnode.from_list(i, "uint256") for i in range(lll_t.count)
            ])

            ret = [
                add_variable_offset(lll_node, k, pos, array_bounds_check=False)
                for k in ks
            ]
        return ret
    else:
        return [lll_node]
Beispiel #9
0
def zero_pad(bytez_placeholder):
    len_ = ["mload", bytez_placeholder]
    dst = ["add", ["add", bytez_placeholder, 32], "len"]
    # the runtime length of the data rounded up to nearest 32
    # from spec:
    #   the actual value of X as a byte sequence,
    #   followed by the *minimum* number of zero-bytes
    #   such that len(enc(X)) is a multiple of 32.
    num_zero_bytes = ["sub", ["ceil32", "len"], "len"]
    return LLLnode.from_list(
        [
            "with", "len", len_,
            ["with", "dst", dst,
             mzero("dst", num_zero_bytes)]
        ],
        annotation="Zero pad",
    )
Beispiel #10
0
def keccak256_helper(expr, args, kwargs, context):
    sub = args[0]
    # Can hash literals
    if isinstance(sub, bytes):
        return LLLnode.from_list(
            bytes_to_int(keccak256(sub)), typ=BaseType("bytes32"), pos=getpos(expr)
        )
    # Can hash bytes32 objects
    if is_base_type(sub.typ, "bytes32"):
        return LLLnode.from_list(
            [
                "seq",
                ["mstore", MemoryPositions.FREE_VAR_SPACE, sub],
                ["sha3", MemoryPositions.FREE_VAR_SPACE, 32],
            ],
            typ=BaseType("bytes32"),
            pos=getpos(expr),
        )
    # Copy the data to an in-memory array
    if sub.location == "memory":
        # If we are hashing a value in memory, no need to copy it, just hash in-place
        return LLLnode.from_list(
            ["with", "_sub", sub, ["sha3", ["add", "_sub", 32], ["mload", "_sub"]]],
            typ=BaseType("bytes32"),
            pos=getpos(expr),
        )
    elif sub.location == "storage":
        lengetter = LLLnode.from_list(["sload", "_sub"], typ=BaseType("int128"))
    else:
        # This should never happen, but just left here for future compiler-writers.
        raise Exception(f"Unsupported location: {sub.location}")  # pragma: no test
    placeholder = context.new_internal_variable(sub.typ)
    placeholder_node = LLLnode.from_list(placeholder, typ=sub.typ, location="memory")
    copier = make_byte_array_copier(
        placeholder_node, LLLnode.from_list("_sub", typ=sub.typ, location=sub.location),
    )
    return LLLnode.from_list(
        ["with", "_sub", sub, ["seq", copier, ["sha3", ["add", placeholder, 32], lengetter]]],
        typ=BaseType("bytes32"),
        pos=getpos(expr),
    )
Beispiel #11
0
def func_init_lll():
    return LLLnode.from_list(STORE_CALLDATA + LIMIT_MEMORY_SET, typ=None)
Beispiel #12
0
def init_func_init_lll():
    return LLLnode.from_list(["seq"] + LIMIT_MEMORY_SET, typ=None)
Beispiel #13
0
def pack_arguments(signature, args, context, stmt_expr, is_external_call):
    pos = getpos(stmt_expr)
    setters = []
    staticarray_offset = 0

    maxlen = sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32
    if is_external_call:
        maxlen += 32

    placeholder_typ = ByteArrayType(maxlen=maxlen)
    placeholder = context.new_internal_variable(placeholder_typ)
    if is_external_call:
        setters.append(["mstore", placeholder, signature.method_id])
        placeholder += 32

    if len(signature.args) != len(args):
        return

    # check for dynamic-length types
    dynamic_remaining = len(
        [i for i in signature.args if isinstance(i.typ, ByteArrayLike)])
    needpos = bool(dynamic_remaining)

    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 + i * 32,
                        typ=typ,
                    ),
                    arg,
                    "memory",
                    pos=pos,
                    in_function_call=True,
                ))

        elif isinstance(typ, ByteArrayLike):
            dynamic_remaining -= 1
            setters.append(
                ["mstore", placeholder + staticarray_offset + i * 32, "_poz"])
            arg_copy = LLLnode.from_list("_s",
                                         typ=arg.typ,
                                         location=arg.location)
            target = LLLnode.from_list(
                ["add", placeholder, "_poz"],
                typ=typ,
                location="memory",
            )
            pos_setter = "pass"

            # if `arg.value` is None, this is a call to `empty()`
            # if `arg.typ.maxlen` is 0, this is a literal "" or b""
            if arg.value is None or arg.typ.maxlen == 0:
                if dynamic_remaining:
                    # only adjust the dynamic pointer if this is not the last dynamic type
                    pos_setter = ["set", "_poz", ["add", "_poz", 64]]
                setters.append(["seq", mzero(target, 64), pos_setter])
            else:
                if dynamic_remaining:
                    pos_setter = [
                        "set",
                        "_poz",
                        [
                            "add", 32,
                            ["ceil32", ["add", "_poz",
                                        get_length(arg_copy)]]
                        ],
                    ]
                setters.append([
                    "with",
                    "_s",
                    arg,
                    [
                        "seq",
                        make_byte_array_copier(target, arg_copy, pos),
                        pos_setter
                    ],
                ])

        elif isinstance(typ, (StructType, ListType)):
            if has_dynamic_data(typ):
                return
            target = LLLnode.from_list(
                [placeholder + staticarray_offset + i * 32],
                typ=typ,
                location="memory",
            )
            setters.append(make_setter(target, arg, "memory", pos=pos))
            staticarray_offset += 32 * (get_size_of_type(typ) - 1)

        else:
            return

    if is_external_call:
        returner = [[placeholder - 4]]
        inargsize = placeholder_typ.maxlen - 28
    else:
        # internal call does not use a returner or adjust max length for signature
        returner = []
        inargsize = placeholder_typ.maxlen

    if needpos:
        return (
            LLLnode.from_list(
                [
                    "with", "_poz",
                    len(args) * 32 + staticarray_offset,
                    ["seq"] + setters + returner
                ],
                typ=placeholder_typ,
                location="memory",
            ),
            inargsize,
            placeholder,
        )
    else:
        return (
            LLLnode.from_list(["seq"] + setters + returner,
                              typ=placeholder_typ,
                              location="memory"),
            inargsize,
            placeholder,
        )
Beispiel #14
0
def get_length(arg):
    if arg.location == "memory":
        return LLLnode.from_list(["mload", arg], typ=BaseType("uint256"))
    elif arg.location == "storage":
        return LLLnode.from_list(["sload", arg], typ=BaseType("uint256"))
Beispiel #15
0
def add_variable_offset(parent, key, pos, array_bounds_check=True):
    typ, location = parent.typ, parent.location
    if isinstance(typ, TupleLike):
        if isinstance(typ, StructType):
            subtype = typ.members[key]
            attrs = list(typ.tuple_keys())
            index = attrs.index(key)
            annotation = key
        else:
            attrs = list(range(len(typ.members)))
            index = key
            annotation = None

        if location == "storage":
            # for arrays and structs, calculate the storage slot by adding an offset
            # of [index value being accessed] * [size of each item within the sequence]
            offset = 0
            for i in range(index):
                offset += get_size_of_type(typ.members[attrs[i]])
            return LLLnode.from_list(
                ["add", parent, offset],
                typ=subtype,
                location="storage",
            )
        elif location == "storage_prehashed":
            return LLLnode.from_list(
                [
                    "add", parent,
                    LLLnode.from_list(index, annotation=annotation)
                ],
                typ=subtype,
                location="storage",
            )
        elif location in ("calldata", "memory"):
            offset = 0
            for i in range(index):
                offset += 32 * get_size_of_type(typ.members[attrs[i]])
            return LLLnode.from_list(
                ["add", offset, parent],
                typ=typ.members[key],
                location=location,
                annotation=annotation,
            )

    elif isinstance(typ, MappingType):

        sub = None
        if isinstance(key.typ, ByteArrayLike):
            if isinstance(
                    typ.keytype,
                    ByteArrayLike) and (typ.keytype.maxlen >= key.typ.maxlen):

                subtype = typ.valuetype
                if len(key.args[0].args) >= 3:  # handle bytes literal.
                    sub = LLLnode.from_list([
                        "seq",
                        key,
                        [
                            "sha3",
                            ["add", key.args[0].args[-1], 32],
                            ["mload", key.args[0].args[-1]],
                        ],
                    ])
                else:
                    value = key.args[0].value
                    if value == "add":
                        # special case, key is a bytes array within a tuple/struct
                        value = key.args[0]
                    sub = LLLnode.from_list(["sha3", ["add", value, 32], key])
        else:
            subtype = typ.valuetype
            sub = unwrap_location(key)

        if sub is not None and location == "storage":
            return LLLnode.from_list(["sha3_64", parent, sub],
                                     typ=subtype,
                                     location="storage")

    elif isinstance(typ, ListType) and is_base_type(
            key.typ, ("int128", "int256", "uint256")):

        subtype = typ.subtype
        k = unwrap_location(key)
        if not array_bounds_check:
            sub = k
        elif key.typ.is_literal:  # note: BaseType always has is_literal attr
            # perform the check at compile time and elide the runtime check.
            if key.value < 0 or key.value >= typ.count:
                return
            sub = k
        else:
            # this works, even for int128. for int128, since two's-complement
            # is used, if the index is negative, (unsigned) LT will interpret
            # it as a very large number, larger than any practical value for
            # an array index, and the clamp will throw an error.
            sub = ["uclamplt", k, typ.count]

        if location == "storage":
            # storage slot determined as [initial storage slot] + [index] * [size of base type]
            offset = get_size_of_type(subtype)
            return LLLnode.from_list(["add", parent, ["mul", sub, offset]],
                                     typ=subtype,
                                     location="storage",
                                     pos=pos)
        elif location == "storage_prehashed":
            return LLLnode.from_list(["add", parent, sub],
                                     typ=subtype,
                                     location="storage",
                                     pos=pos)
        elif location in ("calldata", "memory"):
            offset = 32 * get_size_of_type(subtype)
            return LLLnode.from_list(["add", ["mul", offset, sub], parent],
                                     typ=subtype,
                                     location=location,
                                     pos=pos)
Beispiel #16
0
def parse_tree_to_lll(global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]:
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.name for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
                                        )
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = next((i for i in global_ctx._defs if is_default_func(i)),
                       None)
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]

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

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

    return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
Beispiel #17
0
def make_arg_clamper(datapos, mempos, typ, is_init=False):
    """
    Clamps argument to type limits.

    Arguments
    ---------
    datapos : int | LLLnode
        Calldata offset of the value being clamped
    mempos : int | LLLnode
        Memory offset that the value is stored at during clamping
    typ : vyper.types.types.BaseType
        Type of the value
    is_init : bool, optional
        Boolean indicating if we are generating init bytecode

    Returns
    -------
    LLLnode
        Arg clamper LLL
    """

    if not is_init:
        data_decl = ["calldataload", ["add", 4, datapos]]
        copier = functools.partial(_mk_calldatacopy_copier, mempos=mempos)
    else:
        data_decl = ["codeload", ["add", "~codelen", datapos]]
        copier = functools.partial(_mk_codecopy_copier, mempos=mempos)
    # Numbers: make sure they're in range
    if is_base_type(typ, "int128"):
        return LLLnode.from_list(int128_clamp(data_decl),
                                 typ=typ,
                                 annotation="checking int128 input")
    # Booleans: make sure they're zero or one
    elif is_base_type(typ, "bool"):
        if version_check(begin="constantinople"):
            lll = ["assert", ["iszero", ["shr", 1, data_decl]]]
        else:
            lll = ["uclamplt", data_decl, 2]
        return LLLnode.from_list(lll,
                                 typ=typ,
                                 annotation="checking bool input")
    # Addresses: make sure they're in range
    elif is_base_type(typ, "address"):
        return LLLnode.from_list(address_clamp(data_decl),
                                 typ=typ,
                                 annotation="checking address input")
    # Bytes: make sure they have the right size
    elif isinstance(typ, ByteArrayLike):
        return LLLnode.from_list(
            [
                "seq",
                copier(data_decl, 32 + typ.maxlen),
                [
                    "assert",
                    [
                        "le", ["calldataload", ["add", 4, data_decl]],
                        typ.maxlen
                    ]
                ],
            ],
            typ=None,
            annotation="checking bytearray input",
        )
    # Lists: recurse
    elif isinstance(typ, ListType):
        if typ.count > 5 or (type(datapos) is list and type(mempos) is list):
            # find ultimate base type
            subtype = typ.subtype
            while hasattr(subtype, "subtype"):
                subtype = subtype.subtype

            # make arg clamper for the base type
            offset = MemoryPositions.FREE_LOOP_INDEX
            clamper = make_arg_clamper(
                ["add", datapos, ["mload", offset]],
                ["add", mempos, ["mload", offset]],
                subtype,
                is_init,
            )
            if clamper.value == "pass":
                # no point looping if the base type doesn't require clamping
                return clamper

            # loop the entire array at once, even if it's multidimensional
            type_size = get_size_of_type(typ)
            i_incr = get_size_of_type(subtype) * 32

            mem_to = type_size * 32
            loop_label = f"_check_list_loop_{str(uuid.uuid4())}"

            lll_node = [
                ["mstore", offset, 0],  # init loop
                ["label", loop_label],
                clamper,
                ["mstore", offset, ["add", ["mload", offset], i_incr]],
                [
                    "if", ["lt", ["mload", offset], mem_to],
                    ["goto", loop_label]
                ],
            ]
        else:
            lll_node = []
            for i in range(typ.count):
                offset = get_size_of_type(typ.subtype) * 32 * i
                lll_node.append(
                    make_arg_clamper(datapos + offset, mempos + offset,
                                     typ.subtype, is_init))
        return LLLnode.from_list(["seq"] + lll_node,
                                 typ=None,
                                 annotation="checking list input")
    # Otherwise don't make any checks
    else:
        return LLLnode.from_list("pass")
Beispiel #18
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
Beispiel #19
0
def pack_args_by_32(
    holder,
    maxlen,
    arg,
    typ,
    context,
    placeholder,
    dynamic_offset_counter=None,
    datamem_start=None,
    pos=None,
):
    """
    Copy necessary variables to pre-allocated memory section.

    :param holder: Complete holder for all args
    :param maxlen: Total length in bytes of the full arg section (static + dynamic).
    :param arg: Current arg to pack
    :param context: Context of arg
    :param placeholder: Static placeholder for static argument part.
    :param dynamic_offset_counter: position counter stored in static args.
    :param dynamic_placeholder: pointer to current position in memory to write dynamic values to.
    :param datamem_start: position where the whole datemem section starts.
    """

    if typ.size_in_bytes == 32:
        if isinstance(arg, LLLnode):
            value = unwrap_location(arg)
        else:
            value = Expr(arg, context).lll_node
            value = unwrap_location(value)
        holder.append(
            LLLnode.from_list(["mstore", placeholder, value],
                              location="memory"))
    elif isinstance(typ, ArrayValueAbstractType):

        if isinstance(arg, LLLnode):  # Is prealloacted variable.
            source_lll = arg
        else:
            source_lll = Expr(arg, context).lll_node

        # Set static offset, in arg slot.
        holder.append(
            LLLnode.from_list(
                ["mstore", placeholder, ["mload", dynamic_offset_counter]]))
        # Get the beginning to write the ByteArray to.
        # TODO refactor out the use of `ByteArrayLike` once old types are removed from parser
        dest_placeholder = LLLnode.from_list(
            ["add", datamem_start, ["mload", dynamic_offset_counter]],
            typ=ByteArrayLike(typ.length),
            location="memory",
            annotation="pack_args_by_32:dest_placeholder",
        )
        copier = make_byte_array_copier(dest_placeholder, source_lll, pos=pos)
        holder.append(copier)
        # Add zero padding.
        holder.append(zero_pad(dest_placeholder))

        # Increment offset counter.
        increment_counter = LLLnode.from_list(
            [
                "mstore",
                dynamic_offset_counter,
                [
                    "add",
                    [
                        "add",
                        ["mload", dynamic_offset_counter],
                        ["ceil32", ["mload", dest_placeholder]],
                    ],
                    32,
                ],
            ],
            annotation="Increment dynamic offset counter",
        )
        holder.append(increment_counter)
    elif isinstance(typ, ArrayDefinition):

        if isinstance(arg, vy_ast.Call) and arg.func.get("id") == "empty":
            # special case for `empty()` with a static-sized array
            holder.append(mzero(placeholder, typ.size_in_bytes))
            maxlen += typ.size_in_bytes - 32
            return holder, maxlen

        maxlen += (typ.length - 1) * 32
        typ = typ.value_type

        # NOTE: Below code could be refactored into iterators/getter functions for each type of
        #       repetitive loop. But seeing how each one is a unique for loop, and in which way
        #       the sub value makes the difference in each type of list clearer.

        # List from storage
        if isinstance(arg, vy_ast.Attribute) and arg.value.id == "self":
            stor_list = context.globals[arg.attr]
            size = stor_list.typ.count
            mem_offset = 0
            for i in range(0, size):
                storage_offset = i
                arg2 = LLLnode.from_list([
                    "sload",
                    ["add", Expr(arg, context).lll_node, storage_offset]
                ], )
                holder, maxlen = pack_args_by_32(
                    holder,
                    maxlen,
                    arg2,
                    typ,
                    context,
                    placeholder + mem_offset,
                    pos=pos,
                )
                mem_offset += typ.size_in_bytes

        # List from variable.
        elif isinstance(arg, vy_ast.Name):
            size = context.vars[arg.id].size
            pos = context.vars[arg.id].pos
            mem_offset = 0
            for _ in range(0, size):
                arg2 = LLLnode.from_list(
                    pos + mem_offset, location=context.vars[arg.id].location)
                holder, maxlen = pack_args_by_32(
                    holder,
                    maxlen,
                    arg2,
                    typ,
                    context,
                    placeholder + mem_offset,
                    pos=pos,
                )
                mem_offset += typ.size_in_bytes

        # List from list literal.
        else:
            mem_offset = 0
            for arg2 in arg.elements:
                holder, maxlen = pack_args_by_32(
                    holder,
                    maxlen,
                    arg2,
                    typ,
                    context,
                    placeholder + mem_offset,
                    pos=pos,
                )
                mem_offset += typ.size_in_bytes
    return holder, maxlen
Beispiel #20
0
def external_call(node,
                  context,
                  interface_name,
                  contract_address,
                  pos,
                  value=None,
                  gas=None):
    from vyper.old_codegen.expr import Expr

    if value is None:
        value = 0
    if gas is None:
        gas = "gas"

    method_name = node.func.attr
    sig = context.sigs[interface_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [Expr(arg, context).lll_node for arg in node.args],
        context,
        node.func,
        is_external_call=True,
    )
    output_placeholder, output_size, returner = get_external_call_output(
        sig, context)
    sub = ["seq"]
    if not output_size:
        # if we do not expect return data, check that a contract exists at the target address
        # we can omit this when we _do_ expect return data because we later check `returndatasize`
        sub.append(["assert", ["extcodesize", contract_address]])
    if context.is_constant() and sig.mutability not in ("view", "pure"):
        # TODO this can probably go
        raise StateAccessViolation(
            f"May not call state modifying function '{method_name}' "
            f"within {context.pp_constancy()}.",
            node,
        )

    if context.is_constant() or sig.mutability in ("view", "pure"):
        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,
            ],
        ])
    if output_size:
        # when return data is expected, revert when the length of `returndatasize` is insufficient
        output_type = sig.output_type
        if not has_dynamic_data(output_type):
            static_output_size = get_static_size_of_type(output_type) * 32
            sub.append(
                ["assert", ["gt", "returndatasize", static_output_size - 1]])
        else:
            if isinstance(output_type, ByteArrayLike):
                types_list = (output_type, )
            elif isinstance(output_type, TupleLike):
                types_list = output_type.tuple_members()
            else:
                raise

            dynamic_checks = []
            static_offset = output_placeholder
            static_output_size = 0
            for typ in types_list:
                # ensure length of bytes does not exceed max allowable length for type
                if isinstance(typ, ByteArrayLike):
                    static_output_size += 32
                    # do not perform this check on calls to a JSON interface - we don't know
                    # for certain how long the expected data is
                    if not sig.is_from_json:
                        dynamic_checks.append([
                            "assert",
                            [
                                "lt",
                                [
                                    "mload",
                                    [
                                        "add", ["mload", static_offset],
                                        output_placeholder
                                    ],
                                ],
                                typ.maxlen + 1,
                            ],
                        ])
                static_offset += get_static_size_of_type(typ) * 32
                static_output_size += get_static_size_of_type(typ) * 32

            sub.append(
                ["assert", ["gt", "returndatasize", static_output_size - 1]])
            sub.extend(dynamic_checks)

    sub.extend(returner)

    return LLLnode.from_list(sub,
                             typ=sig.output_type,
                             location="memory",
                             pos=getpos(node))
Beispiel #21
0
def pack_logging_topics(event_id, arg_nodes, arg_types, context):
    topics = [event_id]
    for node, typ in zip(arg_nodes, arg_types):
        value = Expr(node, context).lll_node

        if isinstance(typ, ArrayValueAbstractType):
            if isinstance(node, (vy_ast.Str, vy_ast.Bytes)):
                # for literals, generate the topic at compile time
                value = node.value
                if isinstance(value, str):
                    value = value.encode()
                topics.append(bytes_to_int(keccak256(value)))

            elif value.location == "memory":
                topics.append(["sha3", ["add", value, 32], ["mload", value]])

            else:
                # storage or calldata
                placeholder = context.new_internal_variable(value.typ)
                placeholder_node = LLLnode.from_list(placeholder,
                                                     typ=value.typ,
                                                     location="memory")
                copier = make_byte_array_copier(
                    placeholder_node,
                    LLLnode.from_list("_sub",
                                      typ=value.typ,
                                      location=value.location),
                )
                lll_node = [
                    "with",
                    "_sub",
                    value,
                    [
                        "seq", copier,
                        [
                            "sha3", ["add", placeholder, 32],
                            ["mload", placeholder]
                        ]
                    ],
                ]
                topics.append(lll_node)

        elif isinstance(typ, ArrayDefinition):
            size = typ.size_in_bytes
            if value.location == "memory":
                topics.append(["sha3", value, size])

            else:
                # storage or calldata
                placeholder = context.new_internal_variable(value.typ)
                placeholder_node = LLLnode.from_list(placeholder,
                                                     typ=value.typ,
                                                     location="memory")
                setter = make_setter(placeholder_node, value, "memory",
                                     value.pos)
                lll_node = ["seq", setter, ["sha3", placeholder, size]]
                topics.append(lll_node)

        else:
            value = unwrap_location(value)
            topics.append(value)

    return topics
Beispiel #22
0
def make_call(stmt_expr, context):
    # ** Internal 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

    pop_local_vars = []
    push_local_vars = []
    pop_return_values = []
    push_args = []
    method_name = stmt_expr.func.attr

    # TODO check this out
    from vyper.old_codegen.expr import parse_sequence

    pre_init, expr_args = parse_sequence(stmt_expr, stmt_expr.args, context)
    sig = FunctionSignature.lookup_sig(
        context.sigs,
        method_name,
        expr_args,
        stmt_expr,
        context,
    )

    if context.is_constant() and sig.mutability not in ("view", "pure"):
        raise StateAccessViolation(
            f"May not call state modifying function "
            f"'{method_name}' within {context.pp_constancy()}.",
            getpos(stmt_expr),
        )

    if not sig.internal:
        raise StructureException("Cannot call external functions via 'self'",
                                 stmt_expr)

    # 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])

        if len(var_slots) > 10:
            # if memory is large enough, push and pop it via iteration
            mem_from, mem_to = var_slots[0][
                0], var_slots[-1][0] + var_slots[-1][1] * 32
            i_placeholder = context.new_internal_variable(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
            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:
            # for smaller memory, hardcode the mload/mstore locations
            push_mem_slots = []
            for pos, size in var_slots:
                push_mem_slots.extend([pos + i * 32 for i in range(size)])

            push_local_vars = [["mload", pos] for pos in push_mem_slots]
            pop_local_vars = [["mstore", pos, "pass"]
                              for pos in push_mem_slots[::-1]]

    # Push Arguments
    if expr_args:
        inargs, inargsize, arg_pos = pack_arguments(sig,
                                                    expr_args,
                                                    context,
                                                    stmt_expr,
                                                    is_external_call=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_internal_variable(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 name, typ in sig.output_type.tuple_items():
                    if isinstance(typ, ByteArrayLike):
                        pop_return_values.append([
                            "mstore",
                            ["add", output_placeholder, static_offset], "pass"
                        ])
                        dynamic_offsets.append(([
                            "mload",
                            ["add", output_placeholder, static_offset]
                        ], name))
                        static_offset += 32
                    else:
                        member_output_size = get_size_of_type(typ) * 32
                        pop_return_values.extend([[
                            "mstore", ["add", output_placeholder, pos], "pass"
                        ] for pos in range(static_offset, static_offset +
                                           member_output_size, 32)])
                        static_offset += member_output_size

            # 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_internal_variable(
                    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
Beispiel #23
0
def abi_encode(dst, lll_node, pos=None, bufsz=None, returns=False):
    parent_abi_t = abi_type_of(lll_node.typ)
    size_bound = parent_abi_t.static_size() + parent_abi_t.dynamic_size_bound()
    if bufsz is not None and bufsz < 32 * size_bound:
        raise CompilerPanic("buffer provided to abi_encode not large enough")

    lll_ret = ["seq"]
    dyn_ofst = "dyn_ofst"  # current offset in the dynamic section
    dst_begin = "dst"  # pointer to beginning of buffer
    dst_loc = "dst_loc"  # pointer to write location in static section
    os = o_list(lll_node, pos=pos)

    for i, o in enumerate(os):
        abi_t = abi_type_of(o.typ)

        if parent_abi_t.is_tuple():
            if abi_t.is_dynamic():
                lll_ret.append(["mstore", dst_loc, dyn_ofst])
                # recurse
                child_dst = ["add", dst_begin, dyn_ofst]
                child = abi_encode(child_dst, o, pos=pos, returns=True)
                # increment dyn ofst for the return
                # (optimization note:
                #   if non-returning and this is the last dyn member in
                #   the tuple, this set can be elided.)
                lll_ret.append(["set", dyn_ofst, ["add", dyn_ofst, child]])
            else:
                # recurse
                lll_ret.append(abi_encode(dst_loc, o, pos=pos, returns=False))

        elif isinstance(o.typ, BaseType):
            d = LLLnode(dst_loc, typ=o.typ, location="memory")
            lll_ret.append(make_setter(d, o, location=d.location, pos=pos))
        elif isinstance(o.typ, ByteArrayLike):
            d = LLLnode.from_list(dst_loc, typ=o.typ, location="memory")
            lll_ret.append([
                "seq",
                make_setter(d, o, location=d.location, pos=pos),
                zero_pad(d)
            ])
        else:
            raise CompilerPanic(f"unreachable type: {o.typ}")

        if i + 1 == len(os):
            pass  # optimize out the last increment to dst_loc
        else:  # note: always false for non-tuple types
            sz = abi_t.embedded_static_size()
            lll_ret.append(["set", dst_loc, ["add", dst_loc, sz]])

    # declare LLL variables.
    if returns:
        if not parent_abi_t.is_dynamic():
            lll_ret.append(parent_abi_t.embedded_static_size())
        elif parent_abi_t.is_tuple():
            lll_ret.append("dyn_ofst")
        elif isinstance(lll_node.typ, ByteArrayLike):
            # for abi purposes, return zero-padded length
            calc_len = ["ceil32", ["add", 32, ["mload", dst_loc]]]
            lll_ret.append(calc_len)
        else:
            raise CompilerPanic("unknown type {lll_node.typ}")

    if not (parent_abi_t.is_dynamic() and parent_abi_t.is_tuple()):
        pass  # optimize out dyn_ofst allocation if we don't need it
    else:
        dyn_section_start = parent_abi_t.static_size()
        lll_ret = ["with", "dyn_ofst", dyn_section_start, lll_ret]

    lll_ret = ["with", dst_begin, dst, ["with", dst_loc, dst_begin, lll_ret]]

    return LLLnode.from_list(lll_ret)
Beispiel #24
0
def gen_tuple_return(stmt, context, sub):
    abi_typ = abi_type_of(context.return_type)
    # according to the ABI, return types are ALWAYS tuples even if
    # only one element is being returned.
    # https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector-and-argument-encoding
    # "and the return values v_1, ..., v_k of f are encoded as
    #
    #    enc((v_1, ..., v_k))
    #    i.e. the values are combined into a tuple and encoded.
    # "
    # therefore, wrap it in a tuple if it's not already a tuple.
    # (big difference between returning `(bytes,)` and `bytes`.
    abi_typ = ensure_tuple(abi_typ)
    abi_bytes_needed = abi_typ.static_size() + abi_typ.dynamic_size_bound()
    dst = context.memory_allocator.expand_memory(abi_bytes_needed)
    return_buffer = LLLnode(dst,
                            location="memory",
                            annotation="return_buffer",
                            typ=context.return_type)

    check_assign(return_buffer, sub, pos=getpos(stmt))

    if sub.value == "multi":

        if isinstance(context.return_type,
                      TupleType) and not abi_typ.dynamic_size_bound():
            # for tuples where every value is of the same type and a fixed length,
            # we can simplify the encoding by using make_setter, since
            # our memory encoding happens to be identical to the ABI
            # encoding.
            new_sub = LLLnode.from_list(
                context.new_internal_variable(context.return_type),
                typ=context.return_type,
                location="memory",
            )
            setter = make_setter(new_sub, sub, "memory", pos=getpos(stmt))
            return LLLnode.from_list(
                [
                    "seq",
                    setter,
                    make_return_stmt(
                        stmt,
                        context,
                        new_sub,
                        get_size_of_type(context.return_type) * 32,
                    ),
                ],
                typ=None,
                pos=getpos(stmt),
            )

        # in case of multi we can't create a variable to store location of the return expression
        # as multi can have data from multiple location like store, calldata etc
        encode_out = abi_encode(return_buffer,
                                sub,
                                pos=getpos(stmt),
                                returns=True)
        load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
        os = [
            "seq",
            ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
            make_return_stmt(stmt, context, return_buffer, load_return_len),
        ]
        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)

    # for tuple return types where a function is called inside the tuple, we
    # process the calls prior to encoding the return data
    if sub.value == "seq_unchecked" and sub.args[-1].value == "multi":
        encode_out = abi_encode(return_buffer,
                                sub.args[-1],
                                pos=getpos(stmt),
                                returns=True)
        load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
        os = (["seq"] + sub.args[:-1] + [
            ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
            make_return_stmt(stmt, context, return_buffer, load_return_len),
        ])
        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)

    # for all othe cases we are creating a stack variable named sub_loc to store the  location
    # of the return expression. This is done so that the return expression does not get evaluated
    # abi-encode uses a function named o_list which evaluate the expression multiple times
    sub_loc = LLLnode("sub_loc", typ=sub.typ, location=sub.location)
    encode_out = abi_encode(return_buffer,
                            sub_loc,
                            pos=getpos(stmt),
                            returns=True)
    load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
    os = [
        "with",
        "sub_loc",
        sub,
        [
            "seq",
            ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
            make_return_stmt(stmt, context, return_buffer, load_return_len),
        ],
    ]
    return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)
Beispiel #25
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = unwrap_location(right)
        if location == "storage":
            return LLLnode.from_list(["sstore", left, right], typ=None)
        elif location == "memory":
            return LLLnode.from_list(["mstore", left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatch(
            "Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            return
        if not isinstance(right.typ, ListType):
            return
        if right.typ.count != left.typ.count:
            return

        left_token = LLLnode.from_list("_L",
                                       typ=left.typ,
                                       location=left.location)
        # If the right side is a literal
        if right.value in ["multi", "seq_unchecked"] and right.typ.is_literal:
            if right.value == "seq_unchecked":
                # when the LLL is `seq_unchecked`, this is a literal where one or
                # more values must be pre-processed to avoid memory corruption
                subs = right.args[:-1]
                right = right.args[-1]
            else:
                subs = []
            for i in range(left.typ.count):
                lhs_setter = _make_array_index_setter(left, left_token, pos,
                                                      location, i)
                subs.append(
                    make_setter(
                        lhs_setter,
                        right.args[i],
                        location,
                        pos=pos,
                    ))
            if left.location == "memory" and isinstance(left.value, int):
                return LLLnode.from_list(["seq"] + subs, typ=None)
            else:
                return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                         typ=None)
        elif right.value is None:
            if right.typ != left.typ:
                return
            if left.location == "memory":
                return mzero(left, 32 * get_size_of_type(left.typ))

            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(
                        add_variable_offset(
                            left_token,
                            LLLnode.from_list(i, typ="int256"),
                            pos=pos,
                            array_bounds_check=False,
                        ),
                        LLLnode.from_list(None, typ=right.typ.subtype),
                        location,
                        pos=pos,
                    ))
            return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                     typ=None)
        # If the right side is a variable
        else:
            right_token = LLLnode.from_list("_R",
                                            typ=right.typ,
                                            location=right.location)
            subs = []
            for i in range(left.typ.count):
                lhs_setter = _make_array_index_setter(left, left_token, pos,
                                                      left.location, i)
                rhs_setter = _make_array_index_setter(right, right_token, pos,
                                                      right.location, i)
                subs.append(
                    make_setter(
                        lhs_setter,
                        rhs_setter,
                        location,
                        pos=pos,
                    ))
            lll_node = ["seq"] + subs
            if right.location != "memory" or not isinstance(right.value, int):
                lll_node = ["with", "_R", right, lll_node]
            if left.location != "memory" or not isinstance(left.value, int):
                lll_node = ["with", "_L", left, lll_node]
            return LLLnode.from_list(lll_node, typ=None)
    # Structs
    elif isinstance(left.typ, TupleLike):
        if left.value == "multi" and isinstance(left.typ, StructType):
            return
        if right.value is not None:
            if not isinstance(right.typ, left.typ.__class__):
                return
            if isinstance(left.typ, StructType):
                for k in left.typ.members:
                    if k not in right.typ.members:
                        return
                for k in right.typ.members:
                    if k not in left.typ.members:
                        return
                if left.typ.name != right.typ.name:
                    return
            else:
                if len(left.typ.members) != len(right.typ.members):
                    return

        left_token = LLLnode.from_list("_L",
                                       typ=left.typ,
                                       location=left.location)
        keyz = left.typ.tuple_keys()

        # If the left side is a literal
        if left.value == "multi":
            locations = [arg.location for arg in left.args]
        else:
            locations = [location for _ in keyz]

        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != len(keyz):
                return
            # get the RHS arguments into a dict because
            # they are not guaranteed to be in the same order
            # the LHS keys.
            right_args = dict(zip(right.typ.tuple_keys(), right.args))
            subs = []
            for (key, loc) in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, key, pos=pos),
                        right_args[key],
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                     typ=None)
        # If the right side is a null
        elif right.value is None:
            if left.typ != right.typ:
                return

            if left.location == "memory":
                return mzero(left, 32 * get_size_of_type(left.typ))

            subs = []
            for key, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, key, pos=pos),
                        LLLnode.from_list(None, typ=right.typ.members[key]),
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(["with", "_L", left, ["seq"] + subs],
                                     typ=None)
        # If tuple assign.
        elif isinstance(left.typ, TupleType) and isinstance(
                right.typ, TupleType):
            subs = []
            for var_arg in left.args:
                if var_arg.location == "calldata":
                    return

            right_token = LLLnode.from_list("_R",
                                            typ=right.typ,
                                            location=right.location)
            for left_arg, key, loc in zip(left.args, keyz, locations):
                subs.append(
                    make_setter(left_arg,
                                add_variable_offset(right_token, key, pos=pos),
                                loc,
                                pos=pos))

            return LLLnode.from_list(
                ["with", "_R", right, ["seq"] + subs],
                typ=None,
                annotation="Tuple assignment",
            )
        # If the left side is a variable i.e struct type
        else:
            subs = []
            right_token = LLLnode.from_list("_R",
                                            typ=right.typ,
                                            location=right.location)
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        add_variable_offset(right_token, typ, pos=pos),
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(
                ["with", "_L", left, ["with", "_R", right, ["seq"] + subs]],
                typ=None,
            )
Beispiel #26
0
def make_byte_slice_copier(destination, source, length, max_length, pos=None):
    # Special case: memory to memory
    if source.location == "memory" and destination.location == "memory":
        return LLLnode.from_list(
            [
                "with",
                "_l",
                max_length,
                [
                    "pop",
                    ["call", ["gas"], 4, 0, source, "_l", destination, "_l"]
                ],
            ],
            typ=None,
            annotation=f"copy byte slice dest: {str(destination)}",
        )

    # special case: rhs is zero
    if source.value is None:

        if destination.location == "memory":
            return mzero(destination, max_length)

        else:
            loader = 0
    # Copy over data
    elif source.location == "memory":
        loader = [
            "mload",
            [
                "add", "_pos",
                ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]
            ]
        ]
    elif source.location == "storage":
        loader = [
            "sload",
            ["add", "_pos", ["mload", MemoryPositions.FREE_LOOP_INDEX]]
        ]
    elif source.location == "calldata":
        loader = [
            "calldataload",
            [
                "add", "_pos",
                ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]
            ],
        ]
    else:
        raise CompilerPanic(f"Unsupported location: {source.location}")
    # Where to paste it?
    if destination.location == "memory":
        setter = [
            "mstore",
            [
                "add", "_opos",
                ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]
            ],
            loader,
        ]
    elif destination.location == "storage":
        setter = [
            "sstore",
            ["add", "_opos", ["mload", MemoryPositions.FREE_LOOP_INDEX]],
            loader
        ]
    else:
        raise CompilerPanic(f"Unsupported location: {destination.location}")
    # Check to see if we hit the length
    checker = [
        "if",
        [
            "gt", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]],
            "_actual_len"
        ],
        "break",
    ]
    # Make a loop to do the copying
    ipos = 0 if source.value is None else source
    o = [
        "with",
        "_pos",
        ipos,
        [
            "with",
            "_opos",
            destination,
            [
                "with",
                "_actual_len",
                length,
                [
                    "repeat",
                    MemoryPositions.FREE_LOOP_INDEX,
                    0,
                    (max_length + 31) // 32,
                    ["seq", checker, setter],
                ],
            ],
        ],
    ]
    return LLLnode.from_list(
        o,
        typ=None,
        annotation=f"copy byte slice src: {source} dst: {destination}",
        pos=pos,
    )
Beispiel #27
0
def make_byte_array_copier(destination, source, pos=None):
    if not isinstance(source.typ, ByteArrayLike):
        btype = "byte array" if isinstance(destination.typ,
                                           ByteArrayType) else "string"
        raise TypeMismatch(f"Can only set a {btype} to another {btype}", pos)
    if isinstance(
            source.typ,
            ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen:
        raise TypeMismatch(
            f"Cannot cast from greater max-length {source.typ.maxlen} to shorter "
            f"max-length {destination.typ.maxlen}")

    # stricter check for zeroing a byte array.
    if isinstance(source.typ, ByteArrayLike):
        if source.value is None and source.typ.maxlen != destination.typ.maxlen:
            raise TypeMismatch(
                f"Bad type for clearing bytes: expected {destination.typ}"
                f" but got {source.typ}")

    # Special case: memory to memory
    if source.location == "memory" and destination.location == "memory":
        gas_calculation = GAS_IDENTITY + GAS_IDENTITYWORD * (
            ceil32(source.typ.maxlen) // 32)
        o = LLLnode.from_list(
            [
                "with",
                "_source",
                source,
                [
                    "with",
                    "_sz",
                    ["add", 32, ["mload", "_source"]],
                    [
                        "assert",
                        [
                            "call", ["gas"], 4, 0, "_source", "_sz",
                            destination, "_sz"
                        ]
                    ],
                ],
            ],  # noqa: E501
            typ=None,
            add_gas_estimate=gas_calculation,
            annotation="Memory copy",
        )
        return o

    if source.value is None:
        pos_node = source
    else:
        pos_node = LLLnode.from_list("_pos",
                                     typ=source.typ,
                                     location=source.location)
    # Get the length
    if source.value is None:
        length = 1
    elif source.location == "memory":
        length = ["add", ["mload", "_pos"], 32]
    elif source.location == "storage":
        length = ["add", ["sload", "_pos"], 32]
        pos_node = LLLnode.from_list(
            pos_node,
            typ=source.typ,
            location=source.location,
        )
    else:
        raise CompilerPanic(f"Unsupported location: {source.location}")
    if destination.location == "storage":
        destination = LLLnode.from_list(
            destination,
            typ=destination.typ,
            location=destination.location,
        )
    # Maximum theoretical length
    max_length = 32 if source.value is None else source.typ.maxlen + 32
    return LLLnode.from_list(
        [
            "with",
            "_pos",
            0 if source.value is None else source,
            make_byte_slice_copier(
                destination, pos_node, length, max_length, pos=pos),
        ],
        typ=None,
    )
Beispiel #28
0
def parse_other_functions(o, otherfuncs, sigs, external_interfaces, global_ctx,
                          default_function):
    # check for payable/nonpayable external functions to optimize nonpayable assertions
    func_types = [i._metadata["type"] for i in global_ctx._defs]
    mutabilities = [
        i.mutability for i in func_types
        if i.visibility == FunctionVisibility.EXTERNAL
    ]
    has_payable = next(
        (True for i in mutabilities if i == StateMutability.PAYABLE), False)
    has_nonpayable = next(
        (True for i in mutabilities if i != StateMutability.PAYABLE), False)
    is_default_payable = (default_function is not None
                          and default_function._metadata["type"].mutability
                          == StateMutability.PAYABLE)
    # when a contract has a payable default function and at least one nonpayable
    # external function, we must perform the nonpayable check on every function
    check_per_function = is_default_payable and has_nonpayable

    # generate LLL for regular functions
    payable_func_sub = ["seq"]
    external_func_sub = ["seq"]
    internal_func_sub = ["seq"]
    add_gas = func_init_lll().gas

    for func_node in otherfuncs:
        func_type = func_node._metadata["type"]
        func_lll = parse_function(func_node, {
            **{
                "self": sigs
            },
            **external_interfaces
        }, global_ctx, check_per_function)
        if func_type.visibility == FunctionVisibility.INTERNAL:
            internal_func_sub.append(func_lll)
        elif func_type.mutability == StateMutability.PAYABLE:
            add_gas += 30
            payable_func_sub.append(func_lll)
        else:
            external_func_sub.append(func_lll)
            add_gas += 30
        func_lll.total_gas += add_gas
        for sig in sig_utils.generate_default_arg_sigs(func_node,
                                                       external_interfaces,
                                                       global_ctx):
            sig.gas = func_lll.total_gas
            sigs[sig.sig] = sig

    # generate LLL for fallback function
    if default_function:
        fallback_lll = parse_function(
            default_function,
            {
                **{
                    "self": sigs
                },
                **external_interfaces
            },
            global_ctx,
            # include a nonpayble check here if the contract only has a default function
            check_per_function or not otherfuncs,
        )
    else:
        fallback_lll = LLLnode.from_list(["revert", 0, 0],
                                         typ=None,
                                         annotation="Default function")

    if check_per_function:
        external_seq = ["seq", payable_func_sub, external_func_sub]
    else:
        # payable functions are placed prior to nonpayable functions
        # and seperated by a nonpayable assertion
        external_seq = ["seq"]
        if has_payable:
            external_seq.append(payable_func_sub)
        if has_nonpayable:
            external_seq.extend([["assert", ["iszero", "callvalue"]],
                                 external_func_sub])

    # bytecode is organized by: external functions, fallback fn, internal functions
    # this way we save gas and reduce bytecode by not jumping over internal functions
    main_seq = [
        "seq",
        func_init_lll(),
        ["with", "_func_sig", ["mload", 0], external_seq],
        ["seq_unchecked", ["label", "fallback"], fallback_lll],
        internal_func_sub,
    ]

    o.append(["return", 0, ["lll", main_seq, 0]])
    return o, main_seq
Beispiel #29
0
def parse_internal_function(code: vy_ast.FunctionDef, sig: FunctionSignature,
                            context: Context) -> LLLnode:
    """
    Parse a internal function (FuncDef), and produce full function body.

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

    func_type = code._metadata["type"]

    # Get nonreentrant lock
    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(func_type)

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

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

    _post_callback_ptr = f"{sig.name}_{sig.method_id}_post_callback_ptr"
    context.callback_ptr = context.new_internal_variable(
        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]))

    # internal 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_internal_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.expand_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,
            )

    # internal function copiers. No clamping for internal 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_internal_variable(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 internal_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, internal_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 internal,
            # 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_internal_arg_copier(
                            memory_dest=var.pos,
                            total_size=_size,
                        ))

                # Unpack byte array if necessary.
                if dynamics:
                    i_placeholder = context.new_internal_variable(
                        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",
                    internal_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 internal functions all variable loading occurs in the default
        # function sub routine.
        _clampers = [["label", _post_callback_ptr]]

        # Function with default parameters.
        return LLLnode.from_list(
            [
                "seq",
                sig_chain,
                ["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, internal_label = get_sig_statements(sig, getpos(code))
        return LLLnode.from_list(
            ["seq"] + [internal_label] + nonreentrant_pre + clampers +
            [parse_body(c, context)
             for c in code.body] + nonreentrant_post + stop_func,
            typ=None,
            pos=getpos(code),
        )
Beispiel #30
0
def _add_ofst(loc, ofst):
    if isinstance(loc.value, int):
        return LLLnode(loc.value + ofst)
    return ["add", loc, ofst]