Пример #1
0
def init_func_init_lll():
    return LLLnode.from_list(["seq"] + LIMIT_MEMORY_SET, typ=None)
Пример #2
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):
            if not isinstance(key, str):
                raise TypeMismatch(
                    f"Expecting a member variable access; cannot access element {key}",
                    pos)
            if key not in typ.members:
                raise TypeMismatch(
                    f"Object does not have member variable {key}", pos)
            subtype = typ.members[key]
            attrs = list(typ.tuple_keys())

            if key not in attrs:
                raise TypeMismatch(
                    f"Member {key} not found. Only the following available: " +
                    " ".join(attrs), pos)
            index = attrs.index(key)
            annotation = key
        else:
            if not isinstance(key, int):
                raise TypeMismatch(
                    f"Expecting a static index; cannot access element {key}",
                    pos)
            attrs = list(range(len(typ.members)))
            index = key
            annotation = None

        if location == 'storage':
            return LLLnode.from_list(
                [
                    'add', ['sha3_32', parent],
                    LLLnode.from_list(index, annotation=annotation)
                ],
                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)
        else:
            raise TypeMismatch("Not expecting a member variable access", pos)

    elif isinstance(typ, MappingType):

        if isinstance(key.typ, ByteArrayLike):
            if not isinstance(typ.keytype, ByteArrayLike) or (
                    typ.keytype.maxlen < key.typ.maxlen):
                raise TypeMismatch(
                    "Mapping keys of bytes cannot be cast, use exact same bytes type of: "
                    f"{str(typ.keytype)}",
                    pos,
                )
            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:
                sub = LLLnode.from_list([
                    'sha3', ['add', key.args[0].value, 32],
                    ['mload', key.args[0].value]
                ])
        else:
            subtype = typ.valuetype
            sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos)

        if location == 'storage':
            return LLLnode.from_list(['sha3_64', parent, sub],
                                     typ=subtype,
                                     location='storage')
        elif location in ('memory', 'calldata'):
            raise TypeMismatch(
                "Can only have fixed-side arrays in memory, not mappings", pos)

    elif isinstance(typ, ListType):

        subtype = typ.subtype
        k = unwrap_location(key)
        if not is_base_type(key.typ, ('int128', 'uint256')):
            raise TypeMismatch(f'Invalid type for array index: {key.typ}', pos)

        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:
                raise ArrayIndexException(
                    'Array index determined to be out of bounds. '
                    f'Index is {key.value} but array size is {typ.count}', pos)
            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':
            return LLLnode.from_list(['add', ['sha3_32', parent], sub],
                                     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)
        else:
            raise TypeMismatch("Not expecting an array access ", pos)
    else:
        raise TypeMismatch(
            f"Cannot access the child of a constant variable! {typ}", pos)
Пример #3
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(
            right,
            right.typ,
            left.typ,
            pos,
            in_function_call=in_function_call,
        )
        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":
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, (ListType, NullType)):
            raise TypeMismatch(
                f"Setter type mismatch: left side is array, right side is {right.typ}",
                pos)
        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        # Type checks
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, ListType):
                raise TypeMismatch("Left side is array, right side is not",
                                   pos)
            if left.typ.count != right.typ.count:
                raise TypeMismatch("Mismatched number of elements", pos)
        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != left.typ.count:
                raise TypeMismatch("Mismatched number of elements", pos)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        # CC 20190619 probably not needed as of #1106
        elif isinstance(right.typ, NullType):
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                LLLnode.from_list(None, typ=NullType()),
                                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):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                add_variable_offset(
                                    right_token,
                                    LLLnode.from_list(i, typ='int128'),
                                    pos=pos,
                                    array_bounds_check=False,
                                ),
                                location,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    # Structs
    elif isinstance(left.typ, TupleLike):
        if left.value == "multi" and isinstance(left.typ, StructType):
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, left.typ.__class__):
                raise TypeMismatch(
                    f"Setter type mismatch: left side is {left.typ}, right side is {right.typ}",
                    pos,
                )
            if isinstance(left.typ, StructType):
                for k in right.args:
                    if k.value is None:
                        raise InvalidLiteral(
                            'Setting struct value to None is not allowed, use a default value.',
                            pos,
                        )
                for k in left.typ.members:
                    if k not in right.typ.members:
                        raise TypeMismatch(
                            f"Keys don't match for structs, missing {k}",
                            pos,
                        )
                for k in right.typ.members:
                    if k not in left.typ.members:
                        raise TypeMismatch(
                            f"Keys don't match for structs, extra {k}",
                            pos,
                        )
                if left.typ.name != right.typ.name:
                    raise TypeMismatch(f"Expected {left.typ}, got {right.typ}",
                                       pos)
            else:
                if len(left.typ.members) != len(right.typ.members):
                    raise TypeMismatch(
                        "Tuple lengths don't match, "
                        f"{len(left.typ.members)} vs {len(right.typ.members)}",
                        pos,
                    )

        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        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):
                raise TypeMismatch("Mismatched number of elements", pos)
            # 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 isinstance(right.typ, NullType):
            subs = []
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        LLLnode.from_list(None, typ=NullType()),
                        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 = []
            static_offset_counter = 0
            zipped_components = zip(left.args, right.typ.members, locations)
            for var_arg in left.args:
                if var_arg.location == 'calldata':
                    raise ConstancyViolation(
                        f"Cannot modify function argument: {var_arg.annotation}",
                        pos)
            for left_arg, right_arg, loc in zipped_components:
                if isinstance(right_arg, ByteArrayLike):
                    RType = ByteArrayType if isinstance(
                        right_arg, ByteArrayType) else StringType
                    offset = LLLnode.from_list([
                        'add', '_R',
                        ['mload', ['add', '_R', static_offset_counter]]
                    ],
                                               typ=RType(right_arg.maxlen),
                                               location='memory',
                                               pos=pos)
                    static_offset_counter += 32
                else:
                    offset = LLLnode.from_list(
                        ['mload', ['add', '_R', static_offset_counter]],
                        typ=right_arg.typ,
                        pos=pos,
                    )
                    static_offset_counter += get_size_of_type(right_arg) * 32
                subs.append(make_setter(left_arg, offset, loc, pos=pos))
            return LLLnode.from_list(
                ['with', '_R', right, ['seq'] + subs],
                typ=None,
                annotation='Tuple assignment',
            )
        # If the right side is a variable
        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,
            )
    else:
        raise Exception("Invalid type for setters")
Пример #4
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 treating it as though it were an array
            base_types = set()
            for typ in context.return_type.members:
                while isinstance(typ, ListType):
                    typ = typ.subtype
                base_types.add(typ.typ)

            if len(base_types) == 1:
                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)
Пример #5
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)}')
    # Copy over data
    if isinstance(source.typ, NullType):
        loader = 0
    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]]
        ]
    else:
        raise Exception("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 Exception("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
    o = [
        'with', '_pos', source,
        [
            '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,
    )
Пример #6
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":
            return LLLnode.from_list(
                [
                    "add", ["sha3_32", parent],
                    LLLnode.from_list(index, annotation=annotation)
                ],
                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 = base_type_conversion(key, key.typ, typ.keytype, pos=pos)

        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", "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":
            return LLLnode.from_list(["add", ["sha3_32", parent], sub],
                                     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)
Пример #7
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(
            right,
            right.typ,
            left.typ,
            pos,
            in_function_call=in_function_call,
        )
        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 left.location == "storage":
            left = LLLnode.from_list(["sha3_32", left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        # If the right side is a literal
        if right.value == "multi":
            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="int128"),
                            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)
        if left.location == "storage":
            left = LLLnode.from_list(["sha3_32", left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        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,
            )
Пример #8
0
    def compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.comparators[0], self.context)

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

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

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

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

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

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

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

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

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

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

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

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ='bool',
                                     pos=getpos(self.expr))
        else:
            raise TypeMismatchException(
                "Unsupported types for comparison: %r %r" %
                (left_type, right_type),
                self.expr,
            )
Пример #9
0
def make_external_call(stmt_expr, context):
    from vyper.parser.expr import Expr

    value, gas = get_external_interface_keywords(stmt_expr, context)

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

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

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

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

    elif (isinstance(stmt_expr.func.value, vy_ast.Attribute)
          and stmt_expr.func.value.attr in context.globals
          and hasattr(context.globals[stmt_expr.func.value.attr].typ, "name")):

        contract_name = context.globals[stmt_expr.func.value.attr].typ.name
        var = context.globals[stmt_expr.func.value.attr]
        contract_address = unwrap_location(
            LLLnode.from_list(
                var.pos,
                typ=var.typ,
                location="storage",
                pos=getpos(stmt_expr),
                annotation="self." + stmt_expr.func.value.attr,
            ))

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

    else:
        raise StructureException("Unsupported operator.", stmt_expr)
Пример #10
0
    def number(self):
        orignum = get_original_if_0_prefixed(self.expr, self.context)

        if orignum is None and isinstance(self.expr.n, int):
            # Literal (mostly likely) becomes int128
            if SizeLimits.in_bounds('int128', self.expr.n) or self.expr.n < 0:
                return LLLnode.from_list(
                    self.expr.n,
                    typ=BaseType('int128', unit={}, is_literal=True),
                    pos=getpos(self.expr),
                )
            # Literal is large enough (mostly likely) becomes uint256.
            else:
                return LLLnode.from_list(
                    self.expr.n,
                    typ=BaseType('uint256', unit={}, is_literal=True),
                    pos=getpos(self.expr),
                )

        elif isinstance(self.expr.n, float):
            numstring, num, den = get_number_as_fraction(
                self.expr, self.context)
            # if not SizeLimits.in_bounds('decimal', num // den):
            # if not SizeLimits.MINDECIMAL * den <= num <= SizeLimits.MAXDECIMAL * den:
            if not (SizeLimits.MINNUM * den < num < SizeLimits.MAXNUM * den):
                raise InvalidLiteralException(
                    "Number out of range: " + numstring, self.expr)
            if DECIMAL_DIVISOR % den:
                raise InvalidLiteralException(
                    "Too many decimal places: " + numstring, self.expr)
            return LLLnode.from_list(
                num * DECIMAL_DIVISOR // den,
                typ=BaseType('decimal', unit=None),
                pos=getpos(self.expr),
            )

        # Binary literal.
        elif orignum[:2] == '0b':
            str_val = orignum[2:]
            total_bits = len(orignum[2:])
            total_bits = (
                total_bits if total_bits % 8 == 0 else total_bits + 8 -
                (total_bits % 8)  # ceil8 to get byte length.
            )
            if len(
                    orignum[2:]
            ) != total_bits:  # Support only full formed bit definitions.
                raise InvalidLiteralException(
                    ("Bit notation requires a multiple of 8 bits / 1 byte. {} "
                     "bit(s) are missing.").format(total_bits -
                                                   len(orignum[2:])),
                    self.expr)
            byte_len = int(total_bits / 8)
            placeholder = self.context.new_placeholder(ByteArrayType(byte_len))
            seq = []
            seq.append(['mstore', placeholder, byte_len])
            for i in range(0, total_bits, 256):
                section = str_val[i:i + 256]
                int_val = int(section, 2) << (256 - len(section)
                                              )  # bytes are right padded.
                seq.append(['mstore', ['add', placeholder, i + 32], int_val])
            return LLLnode.from_list(
                ['seq'] + seq + [placeholder],
                typ=ByteArrayType(byte_len),
                location='memory',
                pos=getpos(self.expr),
                annotation='Create ByteArray (Binary literal): %s' % str_val,
            )
        elif len(orignum) == 42:
            if checksum_encode(orignum) != orignum:
                raise InvalidLiteralException(
                    """Address checksum mismatch. If you are sure this is the
right address, the correct checksummed form is: %s""" %
                    checksum_encode(orignum), self.expr)
            return LLLnode.from_list(
                self.expr.n,
                typ=BaseType('address', is_literal=True),
                pos=getpos(self.expr),
            )
        elif len(orignum) == 66:
            return LLLnode.from_list(
                self.expr.n,
                typ=BaseType('bytes32', is_literal=True),
                pos=getpos(self.expr),
            )
        else:
            raise InvalidLiteralException(
                ("Cannot read 0x value with length %d. Expecting 42 (address "
                 "incl 0x) or 66 (bytes32 incl 0x)") % len(orignum), self.expr)
Пример #11
0
    def build_in_comparator(self):
        from vyper.parser.parser import make_setter
        left = Expr(self.expr.left, self.context).lll_node
        right = Expr(self.expr.comparators[0], self.context).lll_node

        if left.typ != right.typ.subtype:
            raise TypeMismatchException(
                "%s cannot be in a list of %s" %
                (left.typ, right.typ.subtype), )

        result_placeholder = self.context.new_placeholder(BaseType('bool'))
        setter = []

        # Load nth item from list in memory.
        if right.value == 'multi':
            # Copy literal to memory to be compared.
            tmp_list = LLLnode.from_list(obj=self.context.new_placeholder(
                ListType(right.typ.subtype, right.typ.count)),
                                         typ=ListType(right.typ.subtype,
                                                      right.typ.count),
                                         location='memory')
            setter = make_setter(tmp_list,
                                 right,
                                 'memory',
                                 pos=getpos(self.expr))
            load_i_from_list = [
                'mload',
                [
                    'add', tmp_list,
                    ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]
                ],
            ]
        elif right.location == "storage":
            load_i_from_list = [
                'sload',
                [
                    'add', ['sha3_32', right],
                    ['mload', MemoryPositions.FREE_LOOP_INDEX]
                ],
            ]
        else:
            load_i_from_list = [
                'mload',
                [
                    'add', right,
                    ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]
                ],
            ]

        # Condition repeat loop has to break on.
        break_loop_condition = [
            'if',
            ['eq', unwrap_location(left), load_i_from_list],
            [
                'seq',
                ['mstore', '_result', 1],  # store true.
                'break'
            ]
        ]

        # Repeat loop to loop-compare each item in the list.
        for_loop_sequence = [['mstore', result_placeholder, 0],
                             [
                                 'with', '_result', result_placeholder,
                                 [
                                     'repeat',
                                     MemoryPositions.FREE_LOOP_INDEX,
                                     0,
                                     right.typ.count,
                                     break_loop_condition,
                                 ]
                             ], ['mload', result_placeholder]]

        # Save list to memory, so one can iterate over it,
        # used when literal was created with tmp_list.
        if setter:
            compare_sequence = ['seq', setter] + for_loop_sequence
        else:
            compare_sequence = ['seq'] + for_loop_sequence

        # Compare the result of the repeat loop to 1, to know if a match was found.
        o = LLLnode.from_list(['eq', 1, compare_sequence],
                              typ='bool',
                              annotation="in comporator")

        return o
Пример #12
0
    def arithmetic(self):
        pre_alloc_left, left = self.arithmetic_get_reference(self.expr.left)
        pre_alloc_right, right = self.arithmetic_get_reference(self.expr.right)

        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            raise TypeMismatchException(
                "Unsupported types for arithmetic op: %r %r" %
                (left.typ, right.typ),
                self.expr,
            )

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

        # Special Case: Simplify any literal to literal arithmetic at compile time.
        if left.typ.is_literal and right.typ.is_literal and \
           isinstance(right.value, int) and isinstance(left.value, int):

            if isinstance(self.expr.op, ast.Add):
                val = left.value + right.value
            elif isinstance(self.expr.op, ast.Sub):
                val = left.value - right.value
            elif isinstance(self.expr.op, ast.Mult):
                val = left.value * right.value
            elif isinstance(self.expr.op, ast.Div):
                val = left.value // right.value
            elif isinstance(self.expr.op, ast.Mod):
                val = left.value % right.value
            elif isinstance(self.expr.op, ast.Pow):
                val = left.value**right.value
            else:
                raise ParserException(
                    'Unsupported literal operator: %s' %
                    str(type(self.expr.op)),
                    self.expr,
                )

            num = ast.Num(val)
            num.source_code = self.expr.source_code
            num.lineno = self.expr.lineno
            num.col_offset = self.expr.col_offset

            return Expr.parse_value_expr(num, self.context)

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

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

        ltyp, rtyp = left.typ.typ, right.typ.typ
        if isinstance(self.expr.op, (ast.Add, ast.Sub)):
            if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}:
                raise TypeMismatchException(
                    "Unit mismatch: %r %r" % (left.typ.unit, right.typ.unit),
                    self.expr,
                )
            if left.typ.positional and right.typ.positional and isinstance(
                    self.expr.op, ast.Add):
                raise TypeMismatchException(
                    "Cannot add two positional units!",
                    self.expr,
                )
            new_unit = left.typ.unit or right.typ.unit

            # xor, as subtracting two positionals gives a delta
            new_positional = left.typ.positional ^ right.typ.positional

            op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub'
            if ltyp == 'uint256' and isinstance(self.expr.op, ast.Add):
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that: a + b >= a
                        ['assert', ['ge', ['add', left, right], left]],
                        ['add', left, right],
                    ],
                    typ=BaseType('uint256', new_unit, new_positional),
                    pos=getpos(self.expr))
            elif ltyp == 'uint256' and isinstance(self.expr.op, ast.Sub):
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that: a >= b
                        ['assert', ['ge', left, right]],
                        ['sub', left, right]
                    ],
                    typ=BaseType('uint256', new_unit, new_positional),
                    pos=getpos(self.expr))
            elif ltyp == rtyp:
                o = LLLnode.from_list(
                    [op, left, right],
                    typ=BaseType(ltyp, new_unit, new_positional),
                    pos=getpos(self.expr),
                )
            else:
                raise Exception("Unsupported Operation '%r(%r, %r)'" %
                                (op, ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Mult):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot multiply positional values!", self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'if',
                    ['eq', left, 0],
                    [0],
                    [
                        'seq',
                        [
                            'assert',
                            ['eq', ['div', ['mul', left, right], left], right]
                        ], ['mul', left, right]
                    ],
                ],
                                      typ=BaseType('uint256', new_unit),
                                      pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                o = LLLnode.from_list(
                    ['mul', left, right],
                    typ=BaseType('int128', new_unit),
                    pos=getpos(self.expr),
                )
            elif ltyp == rtyp == 'decimal':
                o = LLLnode.from_list([
                    'with',
                    'r',
                    right,
                    [
                        'with',
                        'l',
                        left,
                        [
                            'with',
                            'ans',
                            ['mul', 'l', 'r'],
                            [
                                'seq',
                                [
                                    'assert',
                                    [
                                        'or', [
                                            'eq', ['sdiv', 'ans', 'l'], 'r'
                                        ], ['iszero', 'l']
                                    ]
                                ],
                                ['sdiv', 'ans', DECIMAL_DIVISOR],
                            ],
                        ],
                    ],
                ],
                                      typ=BaseType('decimal', new_unit),
                                      pos=getpos(self.expr))
            else:
                raise Exception("Unsupported Operation 'mul(%r, %r)'" %
                                (ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Div):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException("Cannot divide positional values!",
                                            self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit, div=True)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that:  b != 0
                        ['assert', right],
                        ['div', left, right],
                    ],
                    typ=BaseType('uint256', new_unit),
                    pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                o = LLLnode.from_list(
                    ['sdiv', left, ['clamp_nonzero', right]],
                    typ=BaseType('int128', new_unit),
                    pos=getpos(self.expr),
                )
            elif ltyp == rtyp == 'decimal':
                o = LLLnode.from_list([
                    'with', 'l', left,
                    [
                        'with',
                        'r',
                        ['clamp_nonzero', right],
                        [
                            'sdiv',
                            ['mul', 'l', DECIMAL_DIVISOR],
                            'r',
                        ],
                    ]
                ],
                                      typ=BaseType('decimal', new_unit),
                                      pos=getpos(self.expr))
            else:
                raise Exception("Unsupported Operation 'div(%r, %r)'" %
                                (ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Mod):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as modulus arguments!",
                    self.expr,
                )
            if not are_units_compatible(left.typ, right.typ) and not (
                    left.typ.unit or right.typ.unit):  # noqa: E501
                raise TypeMismatchException(
                    "Modulus arguments must have same unit", self.expr)
            new_unit = left.typ.unit or right.typ.unit
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list(
                    ['seq', ['assert', right], ['mod', left, right]],
                    typ=BaseType('uint256', new_unit),
                    pos=getpos(self.expr))
            elif ltyp == rtyp:
                o = LLLnode.from_list(
                    ['smod', left, ['clamp_nonzero', right]],
                    typ=BaseType(ltyp, new_unit),
                    pos=getpos(self.expr),
                )
            else:
                raise Exception("Unsupported Operation 'mod(%r, %r)'" %
                                (ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Pow):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as exponential arguments!",
                    self.expr,
                )
            if right.typ.unit:
                raise TypeMismatchException(
                    "Cannot use unit values as exponents",
                    self.expr,
                )
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(
                    self.expr.right, ast.Name):
                raise TypeMismatchException(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr,
                )
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'seq',
                    [
                        'assert',
                        [
                            'or', ['or', ['eq', right, 1], ['iszero', right]],
                            ['lt', left, ['exp', left, right]]
                        ],
                    ],
                    ['exp', left, right],
                ],
                                      typ=BaseType('uint256'),
                                      pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                new_unit = left.typ.unit
                if left.typ.unit and not isinstance(self.expr.right, ast.Name):
                    new_unit = {
                        left.typ.unit.copy().popitem()[0]: self.expr.right.n
                    }
                o = LLLnode.from_list(
                    ['exp', left, right],
                    typ=BaseType('int128', new_unit),
                    pos=getpos(self.expr),
                )
            else:
                raise TypeMismatchException(
                    'Only whole number exponents are supported', self.expr)
        else:
            raise ParserException(
                "Unsupported binary operator: %r" % self.expr.op, self.expr)

        p = ['seq']

        if pre_alloc_left:
            p.append(pre_alloc_left)
        if pre_alloc_right:
            p.append(pre_alloc_right)

        if o.typ.typ == 'int128':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINNUM],
                o,
                ['mload', MemoryPositions.MAXNUM],
            ])
            return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr))
        elif o.typ.typ == 'decimal':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINDECIMAL],
                o,
                ['mload', MemoryPositions.MAXDECIMAL],
            ])
            return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr))
        if o.typ.typ == 'uint256':
            p.append(o)
            return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr))
        else:
            raise Exception("%r %r" % (o, o.typ))
Пример #13
0
 def attribute(self):
     # x.balance: balance of address x
     if self.expr.attr == 'balance':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatchException(
                 "Type mismatch: balance keyword expects an address as input",
                 self.expr)
         return LLLnode.from_list(
             ['balance', addr],
             typ=BaseType('uint256', {'wei': 1}),
             location=None,
             pos=getpos(self.expr),
         )
     # x.codesize: codesize of address x
     elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatchException(
                 "Type mismatch: codesize keyword expects an address as input",
                 self.expr,
             )
         if self.expr.attr == 'codesize':
             eval_code = ['extcodesize', addr]
             output_type = 'int128'
         else:
             eval_code = ['gt', ['extcodesize', addr], 0]
             output_type = 'bool'
         return LLLnode.from_list(
             eval_code,
             typ=BaseType(output_type),
             location=None,
             pos=getpos(self.expr),
         )
     # self.x: global attribute
     elif isinstance(self.expr.value,
                     ast.Name) and self.expr.value.id == "self":
         if self.expr.attr not in self.context.globals:
             raise VariableDeclarationException(
                 "Persistent variable undeclared: " + self.expr.attr,
                 self.expr,
             )
         var = self.context.globals[self.expr.attr]
         return LLLnode.from_list(
             var.pos,
             typ=var.typ,
             location='storage',
             pos=getpos(self.expr),
             annotation='self.' + self.expr.attr,
         )
     # Reserved keywords
     elif isinstance(
             self.expr.value,
             ast.Name) and self.expr.value.id in ("msg", "block", "tx"):
         key = self.expr.value.id + "." + self.expr.attr
         if key == "msg.sender":
             if self.context.is_private:
                 raise ParserException(
                     "msg.sender not allowed in private functions.",
                     self.expr)
             return LLLnode.from_list(['caller'],
                                      typ='address',
                                      pos=getpos(self.expr))
         elif key == "msg.value":
             if not self.context.is_payable:
                 raise NonPayableViolationException(
                     "Cannot use msg.value in a non-payable function",
                     self.expr,
                 )
             return LLLnode.from_list(
                 ['callvalue'],
                 typ=BaseType('uint256', {'wei': 1}),
                 pos=getpos(self.expr),
             )
         elif key == "msg.gas":
             return LLLnode.from_list(
                 ['gas'],
                 typ='uint256',
                 pos=getpos(self.expr),
             )
         elif key == "block.difficulty":
             return LLLnode.from_list(
                 ['difficulty'],
                 typ='uint256',
                 pos=getpos(self.expr),
             )
         elif key == "block.timestamp":
             return LLLnode.from_list(
                 ['timestamp'],
                 typ=BaseType('uint256', {'sec': 1}, True),
                 pos=getpos(self.expr),
             )
         elif key == "block.coinbase":
             return LLLnode.from_list(['coinbase'],
                                      typ='address',
                                      pos=getpos(self.expr))
         elif key == "block.number":
             return LLLnode.from_list(['number'],
                                      typ='uint256',
                                      pos=getpos(self.expr))
         elif key == "block.prevhash":
             return LLLnode.from_list(
                 ['blockhash', ['sub', 'number', 1]],
                 typ='bytes32',
                 pos=getpos(self.expr),
             )
         elif key == "tx.origin":
             return LLLnode.from_list(['origin'],
                                      typ='address',
                                      pos=getpos(self.expr))
         else:
             raise ParserException("Unsupported keyword: " + key, self.expr)
     # Other variables
     else:
         sub = Expr.parse_variable_location(self.expr.value, self.context)
         # contract type
         if isinstance(sub.typ, ContractType):
             return sub
         if not isinstance(sub.typ, StructType):
             raise TypeMismatchException(
                 "Type mismatch: member variable access not expected",
                 self.expr.value,
             )
         attrs = list(sub.typ.members.keys())
         if self.expr.attr not in attrs:
             raise TypeMismatchException(
                 "Member %s not found. Only the following available: %s" %
                 (self.expr.attr, " ".join(attrs)),
                 self.expr,
             )
         return add_variable_offset(sub,
                                    self.expr.attr,
                                    pos=getpos(self.expr))
Пример #14
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
Пример #15
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,
    )
Пример #16
0
def external_call(node,
                  context,
                  interface_name,
                  contract_address,
                  pos,
                  value=None,
                  gas=None):
    from vyper.parser.expr import Expr

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

    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))
Пример #17
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", ["sha3_32", arg]],
                                 typ=BaseType("uint256"))
Пример #18
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)):
            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 TypeMismatchException(
                        f"Data input bytes are to big: {len(arg.s)} {typ}",
                        pos)

            tmp_variable = context.new_internal_variable(
                f'_log_pack_var_{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=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

    requires_dynamic_offset = any(
        [isinstance(data.typ, ByteArrayLike) for data in expected_data])
    if requires_dynamic_offset:
        dynamic_offset_counter = context.new_placeholder(BaseType(32))
        dynamic_placeholder = context.new_placeholder(BaseType(32))
    else:
        dynamic_offset_counter = 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,
                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,
                            pos=pos)

    return holder, maxlen, dynamic_offset_counter, datamem_start
Пример #19
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,
        )
Пример #20
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 isinstance(typ, BaseType):
        if isinstance(arg, LLLnode):
            value = unwrap_location(arg)
        else:
            value = Expr(arg, context).lll_node
            value = base_type_conversion(value, value.typ, typ, pos)
        holder.append(
            LLLnode.from_list(['mstore', placeholder, value],
                              typ=typ,
                              location='memory'))
    elif isinstance(typ, ByteArrayLike):

        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 biginning to write the ByteArray to.
        dest_placeholder = LLLnode.from_list(
            ['add', datamem_start, ['mload', dynamic_offset_counter]],
            typ=typ,
            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, ListType):
        maxlen += (typ.count - 1) * 32
        typ = typ.subtype

        def check_list_type_match(provided):  # Check list types match.
            if provided != typ:
                raise TypeMismatchException(
                    f"Log list type '{provided}' does not match provided, expected '{typ}'"
                )

        # 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]
            check_list_type_match(stor_list.typ.subtype)
            size = stor_list.typ.count
            mem_offset = 0
            for i in range(0, size):
                storage_offset = i
                arg2 = LLLnode.from_list(
                    [
                        'sload',
                        [
                            'add', ['sha3_32',
                                    Expr(arg, context).lll_node],
                            storage_offset
                        ]
                    ],
                    typ=typ,
                )
                holder, maxlen = pack_args_by_32(
                    holder,
                    maxlen,
                    arg2,
                    typ,
                    context,
                    placeholder + mem_offset,
                    pos=pos,
                )
                mem_offset += get_size_of_type(typ) * 32

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

        # List from list literal.
        else:
            mem_offset = 0
            for arg2 in arg.elts:
                holder, maxlen = pack_args_by_32(
                    holder,
                    maxlen,
                    arg2,
                    typ,
                    context,
                    placeholder + mem_offset,
                    pos=pos,
                )
                mem_offset += get_size_of_type(typ) * 32
    return holder, maxlen
Пример #21
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(
            ["sha3_32", pos_node],
            typ=source.typ,
            location=source.location,
        )
    else:
        raise CompilerPanic(f"Unsupported location: {source.location}")
    if destination.location == "storage":
        destination = LLLnode.from_list(
            ["sha3_32", 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,
    )
Пример #22
0
def add_variable_offset(parent, key, pos):
    typ, location = parent.typ, parent.location
    if isinstance(typ, (StructType, TupleType)):
        if isinstance(typ, StructType):
            if not isinstance(key, str):
                raise TypeMismatchException(
                    "Expecting a member variable access; cannot access element %r"
                    % key, pos)
            if key not in typ.members:
                raise TypeMismatchException(
                    "Object does not have member variable %s" % key, pos)
            subtype = typ.members[key]
            attrs = list(typ.members.keys())

            if key not in attrs:
                raise TypeMismatchException(
                    "Member %s not found. Only the following available: %s" %
                    (key, " ".join(attrs)), pos)
            index = attrs.index(key)
            annotation = key
        else:
            if not isinstance(key, int):
                raise TypeMismatchException(
                    "Expecting a static index; cannot access element %r" % key,
                    pos)
            attrs = list(range(len(typ.members)))
            index = key
            annotation = None
        if location == 'storage':
            return LLLnode.from_list(
                [
                    'add', ['sha3_32', parent],
                    LLLnode.from_list(index, annotation=annotation)
                ],
                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 == '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='memory',
                                     annotation=annotation)
        else:
            raise TypeMismatchException(
                "Not expecting a member variable access", pos)

    elif isinstance(typ, MappingType):

        if isinstance(key.typ, ByteArrayLike):
            if not isinstance(typ.keytype, ByteArrayLike) or (
                    typ.keytype.maxlen < key.typ.maxlen):
                raise TypeMismatchException(
                    'Mapping keys of bytes cannot be cast, use exact same bytes type of: %s'
                    % (str(typ.keytype), ),
                    pos,
                )
            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:
                sub = LLLnode.from_list([
                    'sha3', ['add', key.args[0].value, 32],
                    ['mload', key.args[0].value]
                ])
        else:
            subtype = typ.valuetype
            sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos)

        if location == 'storage':
            return LLLnode.from_list(['sha3_64', parent, sub],
                                     typ=subtype,
                                     location='storage')
        elif location == 'memory':
            raise TypeMismatchException(
                "Can only have fixed-side arrays in memory, not mappings", pos)

    elif isinstance(typ, ListType):

        subtype = typ.subtype
        sub = [
            'uclamplt',
            base_type_conversion(key, key.typ, BaseType('int128'), pos=pos),
            typ.count
        ]

        if location == 'storage':
            return LLLnode.from_list(['add', ['sha3_32', parent], sub],
                                     typ=subtype,
                                     location='storage')
        elif location == 'storage_prehashed':
            return LLLnode.from_list(['add', parent, sub],
                                     typ=subtype,
                                     location='storage')
        elif location == 'memory':
            offset = 32 * get_size_of_type(subtype)
            return LLLnode.from_list(
                ['add', ['mul', offset, sub], parent],
                typ=subtype,
                location='memory',
            )
        else:
            raise TypeMismatchException("Not expecting an array access ", pos)
    else:
        raise TypeMismatchException(
            "Cannot access the child of a constant variable! %r" % typ, pos)
Пример #23
0
def make_external_call(stmt_expr, context):
    from vyper.parser.expr import Expr
    value, gas = get_external_contract_keywords(stmt_expr, context)

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

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

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

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

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

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

    else:
        raise StructureException(
            "Unsupported operator: %r" % ast.dump(stmt_expr), stmt_expr)
Пример #24
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(
            right,
            right.typ,
            left.typ,
            pos,
            in_function_call=in_function_call,
        )
        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 TypeMismatchException(
            "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":
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, (ListType, NullType)):
            raise TypeMismatchException(
                "Setter type mismatch: left side is array, right side is %r" %
                right.typ, pos)
        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        # Type checks
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, ListType):
                raise TypeMismatchException(
                    "Left side is array, right side is not", pos)
            if left.typ.count != right.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
        # If the right side is a literal
        if right.value == "multi":
            if len(right.args) != left.typ.count:
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                    ),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        elif isinstance(right.typ, NullType):
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                    ),
                                LLLnode.from_list(None, typ=NullType()),
                                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):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                    ),
                                add_variable_offset(
                                    right_token,
                                    LLLnode.from_list(i, typ='int128'),
                                    pos=pos,
                                ),
                                location,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    # Structs
    elif isinstance(left.typ, (StructType, TupleType)):
        if left.value == "multi" and isinstance(left.typ, StructType):
            raise Exception("Target of set statement must be a single item")
        if not isinstance(right.typ, NullType):
            if not isinstance(right.typ, left.typ.__class__):
                raise TypeMismatchException(
                    "Setter type mismatch: left side is %r, right side is %r" %
                    (
                        left.typ,
                        right.typ,
                    ),
                    pos,
                )
            if isinstance(left.typ, StructType):
                for k in right.args:
                    if k.value is None:
                        raise InvalidLiteralException(
                            'Setting struct value to None is not allowed, use a default value.',
                            pos,
                        )
                for k in left.typ.members:
                    if k not in right.typ.members:
                        raise TypeMismatchException(
                            "Keys don't match for structs, missing %s" % k,
                            pos,
                        )
                for k in right.typ.members:
                    if k not in left.typ.members:
                        raise TypeMismatchException(
                            "Keys don't match for structs, extra %s" % k,
                            pos,
                        )
                if left.typ.name != right.typ.name:
                    raise TypeMismatchException(
                        "Expected %r, got %r" % (left.typ, right.typ), pos)
            else:
                if len(left.typ.members) != len(right.typ.members):
                    raise TypeMismatchException(
                        "Tuple lengths don't match, %d vs %d" % (
                            len(left.typ.members),
                            len(right.typ.members),
                        ),
                        pos,
                    )

        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        if isinstance(left.typ, StructType):
            keyz = list(left.typ.members.keys())
        else:
            keyz = list(range(len(left.typ.members)))

        # 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):
                raise TypeMismatchException("Mismatched number of elements",
                                            pos)
            subs = []
            for i, (typ, loc) in enumerate(zip(keyz, locations)):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        right.args[i],
                        loc,
                        pos=pos,
                    ))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a null
        elif isinstance(right.typ, NullType):
            subs = []
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        LLLnode.from_list(None, typ=NullType()),
                        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 = []
            static_offset_counter = 0
            zipped_components = zip(left.args, right.typ.members, locations)
            for left_arg, right_arg, loc in zipped_components:
                if isinstance(right_arg, ByteArrayLike):
                    RType = ByteArrayType if isinstance(
                        right_arg, ByteArrayType) else StringType
                    offset = LLLnode.from_list([
                        'add', '_R',
                        ['mload', ['add', '_R', static_offset_counter]]
                    ],
                                               typ=RType(right_arg.maxlen),
                                               location='memory',
                                               pos=pos)
                    static_offset_counter += 32
                else:
                    offset = LLLnode.from_list(
                        ['mload', ['add', '_R', static_offset_counter]],
                        typ=right_arg.typ,
                        pos=pos,
                    )
                    static_offset_counter += get_size_of_type(right_arg) * 32
                subs.append(make_setter(left_arg, offset, loc, pos=pos))
            return LLLnode.from_list(
                ['with', '_R', right, ['seq'] + subs],
                typ=None,
                annotation='Tuple assignment',
            )
        # If the right side is a variable
        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,
            )
    else:
        raise Exception("Invalid type for setters")
Пример #25
0
def get_length(arg):
    if arg.location == "memory":
        return LLLnode.from_list(['mload', arg], typ=BaseType('int128'))
    elif arg.location == "storage":
        return LLLnode.from_list(['sload', ['sha3_32', arg]],
                                 typ=BaseType('int128'))
Пример #26
0
def gen_tuple_return(stmt, context, sub):
    # Is from a call expression.
    if sub.args and len(
            sub.args[0].args) > 0 and sub.args[0].args[0].value == 'call':
        # self-call to public.
        mem_pos = sub.args[0].args[-1]
        mem_size = get_size_of_type(sub.typ) * 32
        return LLLnode.from_list(['return', mem_pos, mem_size], typ=sub.typ)

    elif (sub.annotation and 'Internal Call' in sub.annotation):
        mem_pos = sub.args[
            -1].value if sub.value == 'seq_unchecked' else sub.args[0].args[-1]
        mem_size = get_size_of_type(sub.typ) * 32
        # Add zero padder if bytes are present in output.
        zero_padder = ['pass']
        byte_arrays = [(i, x) for i, x in enumerate(sub.typ.tuple_members())
                       if isinstance(x, ByteArrayLike)]
        if byte_arrays:
            i, x = byte_arrays[-1]
            zero_padder = zero_pad(bytez_placeholder=[
                'add', mem_pos, ['mload', mem_pos + i * 32]
            ],
                                   maxlen=x.maxlen,
                                   context=context)
        return LLLnode.from_list(
            ['seq'] + [sub] + [zero_padder] +
            [make_return_stmt(stmt, context, mem_pos, mem_size)],
            typ=sub.typ,
            pos=getpos(stmt),
            valency=0)

    subs = []
    # Pre-allocate loop_memory_position if required for private function returning.
    loop_memory_position = (context.new_placeholder(
        typ=BaseType('uint256')) if context.is_private else None)
    # Allocate dynamic off set counter, to keep track of the total packed dynamic data size.
    dynamic_offset_counter_placeholder = context.new_placeholder(
        typ=BaseType('uint256'))
    dynamic_offset_counter = LLLnode(
        dynamic_offset_counter_placeholder,
        typ=None,
        annotation="dynamic_offset_counter"  # dynamic offset position counter.
    )
    new_sub = LLLnode.from_list(
        context.new_placeholder(typ=BaseType('uint256')),
        typ=context.return_type,
        location='memory',
        annotation='new_sub',
    )
    left_token = LLLnode.from_list('_loc', typ=new_sub.typ, location="memory")

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

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

    if not isinstance(context.return_type, TupleLike):
        raise TypeMismatchException(
            'Trying to return %r when expecting %r' %
            (sub.typ, context.return_type), getpos(stmt))
    items = context.return_type.tuple_items()

    dynamic_offset_start = 32 * len(items)  # The static list of args end.

    for i, (key, typ) in enumerate(items):
        variable_offset = LLLnode.from_list(
            ['add', 32 * i, left_token],
            typ=typ,
            annotation='variable_offset',
        )  # variable offset of destination
        if sub.typ.is_literal:
            arg = sub.args[i]
        else:
            arg = add_variable_offset(parent=sub, key=key, pos=getpos(stmt))

        if isinstance(typ, ByteArrayLike):
            # Store offset pointer value.
            subs.append(
                ['mstore', variable_offset,
                 get_dynamic_offset_value()])

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

        elif isinstance(typ, BaseType):
            subs.append(
                make_setter(variable_offset, arg, "memory", pos=getpos(stmt)))
        elif isinstance(typ, TupleLike):
            subs.append(gen_tuple_return(stmt, context, arg))
        else:
            # Maybe this should panic because the type error should be
            # caught at an earlier type-checking stage.
            raise TypeMismatchException(
                "Can't return type %s as part of tuple" % arg.typ, stmt)

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

    return LLLnode.from_list([
        'seq', setter,
        make_return_stmt(stmt, context, new_sub, get_dynamic_offset_value(),
                         loop_memory_position)
    ],
                             typ=None,
                             pos=getpos(stmt),
                             valency=0)
Пример #27
0
def pack_arguments(signature,
                   args,
                   context,
                   stmt_expr,
                   return_placeholder=True):
    pos = getpos(stmt_expr)
    placeholder_typ = ByteArrayType(
        maxlen=sum([get_size_of_type(arg.typ)
                    for arg in signature.args]) * 32 + 32)
    placeholder = context.new_placeholder(placeholder_typ)
    setters = [['mstore', placeholder, signature.method_id]]
    needpos = False
    staticarray_offset = 0
    expected_arg_count = len(signature.args)
    actual_arg_count = len(args)
    if actual_arg_count != expected_arg_count:
        raise StructureException(
            f"Wrong number of args for: {signature.name} "
            f"({actual_arg_count} args given, expected {expected_arg_count}",
            stmt_expr)

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

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

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

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

    # For private call usage, doesn't use a returner.
    returner = [[placeholder + 28]] if return_placeholder else []
    if needpos:
        return (LLLnode.from_list([
            'with', '_poz',
            len(args) * 32 + staticarray_offset, ['seq'] + setters + returner
        ],
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
    else:
        return (LLLnode.from_list(['seq'] + setters + returner,
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
Пример #28
0
def call_self_private(stmt_expr, context, sig):
    # ** Private Call **
    # Steps:
    # (x) push current local variables
    # (x) push arguments
    # (x) push jumpdest (callback ptr)
    # (x) jump to label
    # (x) pop return values
    # (x) pop local variables

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

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

        i_placeholder = context.new_placeholder(BaseType('uint256'))
        local_save_ident = "_%d_%d" % (stmt_expr.lineno, stmt_expr.col_offset)
        push_loop_label = 'save_locals_start' + local_save_ident
        pop_loop_label = 'restore_locals_start' + local_save_ident

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

    # Push Arguments
    if expr_args:
        inargs, inargsize, arg_pos = pack_arguments(
            sig,
            expr_args,
            context,
            return_placeholder=False,
            pos=getpos(stmt_expr),
        )
        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 = arg_pos + static_arg_size
        needs_dyn_section = any(
            [has_dynamic_data(arg.typ) for arg in expr_args])

        if needs_dyn_section:
            ident = 'push_args_%d_%d_%d' % (sig.method_id, stmt_expr.lineno,
                                            stmt_expr.col_offset)
            start_label = ident + '_start'
            end_label = ident + '_end'
            i_placeholder = context.new_placeholder(BaseType('uint256'))

            # Calculate copy start position.
            # Given | static | dynamic | section in memory,
            # copy backwards so the values are in order on the stack.
            # We calculate i, the end of the whole encoded part
            # (i.e. the starting index for copy)
            # by taking ceil32(len<arg>) + offset<arg> + arg_pos
            # for the last dynamic argument and arg_pos is the start
            # the whole argument section.
            for idx, arg in enumerate(expr_args):
                if isinstance(arg.typ, ByteArrayLike):
                    last_idx = idx
            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))]

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

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

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

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

    o = LLLnode.from_list(pop_returner_call_body,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(stmt_expr),
                          annotation='Internal Call: %s' % method_name,
                          add_gas_estimate=sig.gas)
    o.gas += sig.gas
    return o
Пример #29
0
def make_byte_array_copier(destination, source, pos=None):
    if not isinstance(source.typ, (ByteArrayLike, NullType)):
        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}")
    # 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

    pos_node = LLLnode.from_list('_pos',
                                 typ=source.typ,
                                 location=source.location)
    # Get the length
    if isinstance(source.typ, NullType):
        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(
            ['sha3_32', pos_node],
            typ=source.typ,
            location=source.location,
        )
    else:
        raise Exception("Unsupported location:" + source.location)
    if destination.location == "storage":
        destination = LLLnode.from_list(
            ['sha3_32', destination],
            typ=destination.typ,
            location=destination.location,
        )
    # Maximum theoretical length
    max_length = 32 if isinstance(source.typ,
                                  NullType) else source.typ.maxlen + 32
    return LLLnode.from_list([
        'with', '_pos', 0 if isinstance(source.typ, NullType) else source,
        make_byte_slice_copier(
            destination, pos_node, length, max_length, pos=pos)
    ],
                             typ=None)
Пример #30
0
def func_init_lll():
    return LLLnode.from_list(STORE_CALLDATA + LIMIT_MEMORY_SET, typ=None)