Пример #1
0
    def parse_BinOp(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            return

        arithmetic_pair = {left.typ.typ, right.typ.typ}
        pos = getpos(self.expr)

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

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

        if left.typ.typ == "decimal" and isinstance(self.expr.op, vy_ast.Pow):
            return

        # Only allow explicit conversions to occur.
        if left.typ.typ != right.typ.typ:
            return

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

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

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

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

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

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

            elif ltyp == rtyp == "decimal":
                # TODO should this be smul
                arith = [
                    "with",
                    "ans",
                    ["mul", "l", "r"],
                    [
                        "seq",
                        [
                            "assert",
                            [
                                "or", ["eq", ["sdiv", "ans", "l"], "r"],
                                ["iszero", "l"]
                            ]
                        ],
                        ["sdiv", "ans", DECIMAL_DIVISOR],
                    ],
                ]

        elif isinstance(self.expr.op, vy_ast.Div):
            if right.typ.is_literal and right.value == 0:
                return

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

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

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

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                return

            new_typ = BaseType(ltyp)

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

        elif isinstance(self.expr.op, vy_ast.Pow):
            if ltyp != "int128" and ltyp != "uint256" and isinstance(
                    self.expr.right, vy_ast.Name):
                return
            new_typ = BaseType(ltyp)

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

        if arith is None:
            return

        p = ["seq"]
        if new_typ.typ == "int128":
            p.append([
                "clamp",
                ["mload", MemoryPositions.MINNUM],
                arith,
                ["mload", MemoryPositions.MAXNUM],
            ])
        elif new_typ.typ == "decimal":
            p.append([
                "clamp",
                ["mload", MemoryPositions.MINDECIMAL],
                arith,
                ["mload", MemoryPositions.MAXDECIMAL],
            ])
        elif new_typ.typ == "uint256":
            p.append(arith)
        else:
            return

        p = ["with", "l", left, ["with", "r", right, p]]
        return LLLnode.from_list(p, typ=new_typ, pos=pos)
Пример #2
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'))
Пример #3
0
def pack_arguments(signature, args, context, pos, return_placeholder=True):
    placeholder_typ = ByteArrayType(
        maxlen=sum([get_size_of_type(arg.typ)
                    for arg in signature.args]) * 32 + 32)
    placeholder = context.new_placeholder(placeholder_typ)
    setters = [['mstore', placeholder, signature.method_id]]
    needpos = False
    staticarray_offset = 0
    expected_arg_count = len(signature.args)
    actual_arg_count = len(args)
    if actual_arg_count != expected_arg_count:
        raise StructureException(
            "Wrong number of args for: %s (%s args, expected %s)" % (
                signature.name,
                actual_arg_count,
                expected_arg_count,
            ))

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

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

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

        else:
            raise TypeMismatchException("Cannot pack argument of type %r" %
                                        typ)

    # 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)
Пример #4
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = unwrap_location(right)
        if location == "storage":
            return LLLnode.from_list(["sstore", left, right], typ=None)
        elif location == "memory":
            return LLLnode.from_list(["mstore", left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatch("Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            return
        if not isinstance(right.typ, ListType):
            return
        if right.typ.count != left.typ.count:
            return

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

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

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

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

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

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

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

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

            return LLLnode.from_list(
                ["with", "_R", right, ["seq"] + subs], typ=None, annotation="Tuple assignment",
            )
        # If the left side is a variable i.e struct type
        else:
            subs = []
            right_token = LLLnode.from_list("_R", typ=right.typ, location=right.location)
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(
                        add_variable_offset(left_token, typ, pos=pos),
                        add_variable_offset(right_token, typ, pos=pos),
                        loc,
                        pos=pos,
                    )
                )
            return LLLnode.from_list(
                ["with", "_L", left, ["with", "_R", right, ["seq"] + subs]], typ=None,
            )
Пример #5
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)
Пример #6
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,
    )
Пример #7
0
def add_variable_offset(parent, key, pos, array_bounds_check=True):
    typ, location = parent.typ, parent.location
    if isinstance(typ, TupleLike):
        if isinstance(typ, StructType):
            subtype = typ.members[key]
            attrs = list(typ.tuple_keys())
            index = attrs.index(key)
            annotation = key
        else:
            attrs = list(range(len(typ.members)))
            index = key
            annotation = None

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

    elif isinstance(typ, MappingType):

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

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

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

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

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

        if location == "storage":
            # storage slot determined as [initial storage slot] + [index] * [size of base type]
            offset = get_size_of_type(subtype)
            return LLLnode.from_list(
                ["add", parent, ["mul", sub, offset]], typ=subtype, location="storage", pos=pos
            )
        elif location == "storage_prehashed":
            return LLLnode.from_list(["add", parent, sub], typ=subtype, location="storage", pos=pos)
        elif location in ("calldata", "memory"):
            offset = 32 * get_size_of_type(subtype)
            return LLLnode.from_list(
                ["add", ["mul", offset, sub], parent], typ=subtype, location=location, pos=pos
            )
Пример #8
0
def func_init_lll():
    return LLLnode.from_list(STORE_CALLDATA + LIMIT_MEMORY_SET, typ=None)
Пример #9
0
def init_func_init_lll():
    return LLLnode.from_list(["seq"] + LIMIT_MEMORY_SET, typ=None)
Пример #10
0
    def compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.comparators[0], self.context)

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

        if isinstance(left.typ, 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):
                raise TypeMismatchException(
                    "Can't compare values with different units!", self.expr)

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

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

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

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

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

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

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

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

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

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ='bool',
                                     pos=getpos(self.expr))
        else:
            raise TypeMismatchException(
                "Unsupported types for comparison: %r %r" %
                (left_type, right_type), self.expr)
Пример #11
0
def parse_tree_to_lll(source_code: str,
                      global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]:
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.name for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
                                        )
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)]
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]
    sigs: dict = {}
    external_interfaces: dict = {}
    # Create the main statement
    o = ["seq"]
    if global_ctx._events:
        sigs = parse_events(sigs, global_ctx)
    if global_ctx._contracts or global_ctx._interfaces:
        external_interfaces = parse_external_interfaces(
            external_interfaces, global_ctx)
    # If there is an init func...
    if initfunc:
        o.append(init_func_init_lll())
        o.append(
            parse_function(
                initfunc[0],
                {
                    **{
                        "self": sigs
                    },
                    **external_interfaces
                },
                source_code,
                global_ctx,
            ))

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

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

    return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
Пример #12
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 Exception("Unsupported keyword: " + key)
     # 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))
Пример #13
0
    def parse_Compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

        if right.value is None:
            return

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

        elif isinstance(self.expr.op, vy_ast.In) and isinstance(
                right.typ, ListType):
            if left.typ != right.typ.subtype:
                return
            return self.build_in_comparator()

        if isinstance(self.expr.op, vy_ast.Gt):
            op = "sgt"
        elif isinstance(self.expr.op, vy_ast.GtE):
            op = "sge"
        elif isinstance(self.expr.op, vy_ast.LtE):
            op = "sle"
        elif isinstance(self.expr.op, vy_ast.Lt):
            op = "slt"
        elif isinstance(self.expr.op, vy_ast.Eq):
            op = "eq"
        elif isinstance(self.expr.op, vy_ast.NotEq):
            op = "ne"
        else:
            return

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

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

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

                else:
                    return

            else:

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

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

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ("eq", "ne"):
                return
        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
            return

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ="bool",
                                     pos=getpos(self.expr))
Пример #14
0
    def build_in_comparator(self):
        left = Expr(self.expr.left, self.context).lll_node
        right = Expr(self.expr.right, self.context).lll_node

        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], "break"],  # store true.
        ]

        # 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.
        lll_node = LLLnode.from_list(["eq", 1, compare_sequence],
                                     typ="bool",
                                     annotation="in comporator")

        return lll_node
Пример #15
0
    def build_in_comparator(self):
        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 TypeMismatch(
                f"{left.typ} cannot be in a list of {right.typ.subtype}",
                self.expr,
            )

        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
Пример #16
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)
Пример #17
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 InvalidLiteral(
                'Comparison to None is not allowed, compare against a default value.',
                self.expr,
            )

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

        elif isinstance(self.expr.ops[0], vy_ast.In) and isinstance(
                right.typ, ListType):
            if left.typ != right.typ.subtype:
                raise TypeMismatch(
                    "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 TypeMismatch(
                    "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], vy_ast.Gt):
            op = 'sgt'
        elif isinstance(self.expr.ops[0], vy_ast.GtE):
            op = 'sge'
        elif isinstance(self.expr.ops[0], vy_ast.LtE):
            op = 'sle'
        elif isinstance(self.expr.ops[0], vy_ast.Lt):
            op = 'slt'
        elif isinstance(self.expr.ops[0], vy_ast.Eq):
            op = 'eq'
        elif isinstance(self.expr.ops[0], vy_ast.NotEq):
            op = 'ne'
        else:
            raise Exception("Unsupported comparison operator")

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

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

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

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

            else:

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

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

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ('eq', 'ne'):
                raise TypeMismatch("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 TypeMismatch(
                f'Implicit conversion from {left_type} to {right_type} disallowed, please convert.',
                self.expr,
            )

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ='bool',
                                     pos=getpos(self.expr))
        else:
            raise TypeMismatch(
                f"Unsupported types for comparison: {left_type} {right_type}",
                self.expr,
            )
Пример #18
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 TypeMismatchException(
            "Can only set a {} to another {}".format(btype, btype), pos)
    if isinstance(
            source.typ,
            ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen:
        raise TypeMismatchException(
            "Cannot cast from greater max-length %d to shorter max-length %d" %
            (
                source.typ.maxlen,
                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', ['add', 18, ['div', '_sz', 10]], 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)
Пример #19
0
def get_length(arg):
    if arg.location == "memory":
        return LLLnode.from_list(["mload", arg], typ=BaseType("uint256"))
    elif arg.location == "storage":
        return LLLnode.from_list(["sload", arg], typ=BaseType("uint256"))
Пример #20
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.
    if context.vars:
        var_slots = [(v.pos, v.size) for name, v in context.vars.items()]
        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
        push_local_vars = [['mload', pos]
                           for pos in range(mem_from, mem_to, 32)]
        pop_local_vars = [['mstore', pos, 'pass']
                          for pos in reversed(range(mem_from, mem_to, 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_count = len(expr_args) * 32
        static_pos = arg_pos + static_arg_count
        total_arg_size = ceil32(inargsize - 4)

        if len(expr_args) * 32 != total_arg_size:  # requires dynamic 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'))
            push_args += [
                ['mstore', i_placeholder, arg_pos + total_arg_size],
                ['label', start_label],
                [
                    'if', ['lt', ['mload', i_placeholder], static_pos],
                    ['goto', end_label]
                ],
                [
                    'if_unchecked',
                    ['ne', ['mload', ['mload', i_placeholder]], 0],
                    ['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, ByteArrayType):
                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, ByteArrayType):
                        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],
                        [
                            'if',
                            [
                                'ge', ['mload', i_placeholder],
                                ['ceil32', ['mload', begin_pos]]
                            ], ['goto', end_label]
                        ],  # break
                        [
                            'mstore',
                            [
                                'add', ['add', begin_pos, 32],
                                ['mload', i_placeholder]
                            ], 'pass'
                        ],  # pop into correct memory slot.
                        [
                            'mstore', i_placeholder,
                            ['add', 32, ['mload', i_placeholder]]
                        ],  # increment i
                        ['goto', start_label],
                        ['label', end_label]
                    ],
                    typ=None,
                    annotation='dynamic unpacker',
                    pos=getpos(stmt_expr))
                pop_return_values.append(o)

    call_body = (['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
Пример #21
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,
        )
Пример #22
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) and \
           arithmetic_pair.issubset({'uint256', 'int128'}):

            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(n=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),
                )

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

        # 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))
Пример #23
0
def make_byte_array_copier(destination, source, pos=None):
    if not isinstance(source.typ, ByteArrayLike):
        btype = "byte array" if isinstance(destination.typ, ByteArrayType) else "string"
        raise TypeMismatch(f"Can only set a {btype} to another {btype}", pos)
    if isinstance(source.typ, ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen:
        raise TypeMismatch(
            f"Cannot cast from greater max-length {source.typ.maxlen} to shorter "
            f"max-length {destination.typ.maxlen}"
        )

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

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

    if source.value is None:
        pos_node = source
    else:
        pos_node = LLLnode.from_list("_pos", typ=source.typ, location=source.location)
    # Get the length
    if source.value is None:
        length = 1
    elif source.location == "memory":
        length = ["add", ["mload", "_pos"], 32]
    elif source.location == "storage":
        length = ["add", ["sload", "_pos"], 32]
        pos_node = LLLnode.from_list(pos_node, typ=source.typ, location=source.location,)
    else:
        raise CompilerPanic(f"Unsupported location: {source.location}")
    if destination.location == "storage":
        destination = LLLnode.from_list(
            destination, typ=destination.typ, location=destination.location,
        )
    # Maximum theoretical length
    max_length = 32 if source.value is None else source.typ.maxlen + 32
    return LLLnode.from_list(
        [
            "with",
            "_pos",
            0 if source.value is None else source,
            make_byte_slice_copier(destination, pos_node, length, max_length, pos=pos),
        ],
        typ=None,
    )
Пример #24
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, is_literal=True),
                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)
Пример #25
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', 18 + max_length // 10, 4, 0, source, '_l',
                    destination, '_l'
                ]
            ]
        ],
                                 typ=None,
                                 annotation='copy byte slice dest: %s' %
                                 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='copy byte slice src: %s dst: %s' % (source, destination),
        pos=pos,
    )
Пример #26
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 TypeMismatch(
                 "Type mismatch: balance keyword expects an address as input",
                 self.expr)
         if (isinstance(self.expr.value, vy_ast.Name)
                 and self.expr.value.id == "self"
                 and version_check(begin="istanbul")):
             seq = ['selfbalance']
         else:
             seq = ['balance', addr]
         return LLLnode.from_list(
             seq,
             typ=BaseType('uint256', {'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 TypeMismatch(
                 "Type mismatch: codesize keyword expects an address as input",
                 self.expr,
             )
         if self.expr.attr == 'codesize':
             eval_code = ['extcodesize', addr]
             output_type = 'int128'
         else:
             eval_code = ['gt', ['extcodesize', addr], 0]
             output_type = 'bool'
         return LLLnode.from_list(
             eval_code,
             typ=BaseType(output_type),
             location=None,
             pos=getpos(self.expr),
         )
     # x.codehash: keccak of address x
     elif self.expr.attr == 'codehash':
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not is_base_type(addr.typ, 'address'):
             raise TypeMismatch(
                 "codehash keyword expects an address as input",
                 self.expr,
             )
         if not version_check(begin="constantinople"):
             raise EvmVersionException(
                 "address.codehash is unavailable prior to constantinople ruleset",
                 self.expr)
         return LLLnode.from_list(['extcodehash', addr],
                                  typ=BaseType('bytes32'),
                                  location=None,
                                  pos=getpos(self.expr))
     # self.x: global attribute
     elif isinstance(self.expr.value,
                     vy_ast.Name) and self.expr.value.id == "self":
         if self.expr.attr not in self.context.globals:
             raise VariableDeclarationException(
                 "Persistent variable undeclared: " + self.expr.attr,
                 self.expr,
             )
         var = self.context.globals[self.expr.attr]
         return LLLnode.from_list(
             var.pos,
             typ=var.typ,
             location='storage',
             pos=getpos(self.expr),
             annotation='self.' + self.expr.attr,
         )
     # Reserved keywords
     elif (isinstance(self.expr.value, vy_ast.Name)
           and self.expr.value.id in ENVIRONMENT_VARIABLES):
         key = self.expr.value.id + "." + self.expr.attr
         if key == "msg.sender":
             if self.context.is_private:
                 raise StructureException(
                     "msg.sender not allowed in private functions.",
                     self.expr)
             return LLLnode.from_list(['caller'],
                                      typ='address',
                                      pos=getpos(self.expr))
         elif key == "msg.value":
             if not self.context.is_payable:
                 raise NonPayableViolation(
                     "Cannot use msg.value in a non-payable function",
                     self.expr,
                 )
             return LLLnode.from_list(
                 ['callvalue'],
                 typ=BaseType('uint256', {'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))
         elif key == "chain.id":
             if not version_check(begin="istanbul"):
                 raise EvmVersionException(
                     "chain.id is unavailable prior to istanbul ruleset",
                     self.expr)
             return LLLnode.from_list(['chainid'],
                                      typ='uint256',
                                      pos=getpos(self.expr))
         else:
             raise StructureException("Unsupported keyword: " + key,
                                      self.expr)
     # Other variables
     else:
         sub = Expr.parse_variable_location(self.expr.value, self.context)
         # contract type
         if isinstance(sub.typ, ContractType):
             return sub
         if not isinstance(sub.typ, StructType):
             raise TypeMismatch(
                 "Type mismatch: member variable access not expected",
                 self.expr.value,
             )
         attrs = list(sub.typ.members.keys())
         if self.expr.attr not in attrs:
             raise TypeMismatch(
                 f"Member {self.expr.attr} not found. Only the following available: "
                 f"{' '.join(attrs)}",
                 self.expr,
             )
         return add_variable_offset(sub,
                                    self.expr.attr,
                                    pos=getpos(self.expr))
Пример #27
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
        k = unwrap_location(key)
        if not is_base_type(key.typ, ('int128', 'uint256')):
            raise TypeMismatchException(
                'Invalid type for array index: %r' % key.typ, pos)

        if 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. '
                    'Index is %r but array size is %r' %
                    (key.value, 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')
        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)
Пример #28
0
    def arithmetic(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

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

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

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

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

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

            return Expr.parse_value_expr(num, self.context)

        pos = getpos(self.expr)

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

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

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

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

        ltyp, rtyp = left.typ.typ, right.typ.typ
        if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)):
            if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}:
                raise TypeMismatch(
                    f"Unit mismatch: {left.typ.unit} {right.typ.unit}",
                    self.expr,
                )
            if (left.typ.positional and right.typ.positional
                    and isinstance(self.expr.op, vy_ast.Add)):
                raise TypeMismatch(
                    "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

            new_typ = BaseType(ltyp, new_unit, new_positional)

            op = 'add' if isinstance(self.expr.op, vy_ast.Add) else 'sub'

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

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

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

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

        elif isinstance(self.expr.op, vy_ast.Mult):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatch("Cannot multiply positional values!",
                                   self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit)
            new_typ = BaseType(ltyp, new_unit)

            if ltyp == rtyp == 'uint256':
                arith = [
                    'with', 'ans', ['mul', 'l', 'r'],
                    [
                        'seq',
                        [
                            'assert',
                            [
                                'or', ['eq', ['div', 'ans', 'l'], 'r'],
                                ['iszero', 'l']
                            ]
                        ], 'ans'
                    ]
                ]

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

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

        elif isinstance(self.expr.op, vy_ast.Div):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot divide by 0.", self.expr)
            if left.typ.positional or right.typ.positional:
                raise TypeMismatch("Cannot divide positional values!",
                                   self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit, div=True)
            new_typ = BaseType(ltyp, new_unit)
            if ltyp == rtyp == 'uint256':
                arith = ['div', 'l', ['clamp_nonzero', 'r']]

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

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

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

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot calculate modulus of 0.",
                                            self.expr)
            if left.typ.positional or right.typ.positional:
                raise TypeMismatch(
                    "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 TypeMismatch("Modulus arguments must have same unit",
                                   self.expr)
            new_unit = left.typ.unit or right.typ.unit
            new_typ = BaseType(ltyp, new_unit)

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

            else:
                raise Exception(f"Unsupported Operation 'mod({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, vy_ast.Pow):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatch(
                    "Cannot use positional values as exponential arguments!",
                    self.expr,
                )
            if right.typ.unit:
                raise TypeMismatch(
                    "Cannot use unit values as exponents",
                    self.expr,
                )
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(
                    self.expr.right, vy_ast.Name):
                raise TypeMismatch(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr,
                )
            new_unit = left.typ.unit
            if left.typ.unit and not isinstance(self.expr.right, vy_ast.Name):
                new_unit = {
                    left.typ.unit.copy().popitem()[0]: self.expr.right.n
                }
            new_typ = BaseType(ltyp, new_unit)

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

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

        p = ['seq']

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

        p = ['with', 'l', left, ['with', 'r', right, p]]
        return LLLnode.from_list(p, typ=new_typ, pos=pos)
Пример #29
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")
Пример #30
0
 def parse_Attribute(self):
     # x.balance: balance of address x
     if self.expr.attr == "balance":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if is_base_type(addr.typ, "address"):
             if (isinstance(self.expr.value, vy_ast.Name)
                     and self.expr.value.id == "self"
                     and version_check(begin="istanbul")):
                 seq = ["selfbalance"]
             else:
                 seq = ["balance", addr]
             return LLLnode.from_list(
                 seq,
                 typ=BaseType("uint256"),
                 location=None,
                 pos=getpos(self.expr),
             )
     # x.codesize: codesize of address x
     elif self.expr.attr == "codesize" or self.expr.attr == "is_contract":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if is_base_type(addr.typ, "address"):
             if self.expr.attr == "codesize":
                 eval_code = ["extcodesize", addr]
                 output_type = "int128"
             else:
                 eval_code = ["gt", ["extcodesize", addr], 0]
                 output_type = "bool"
             return LLLnode.from_list(
                 eval_code,
                 typ=BaseType(output_type),
                 location=None,
                 pos=getpos(self.expr),
             )
     # x.codehash: keccak of address x
     elif self.expr.attr == "codehash":
         addr = Expr.parse_value_expr(self.expr.value, self.context)
         if not version_check(begin="constantinople"):
             raise EvmVersionException(
                 "address.codehash is unavailable prior to constantinople ruleset",
                 self.expr)
         if is_base_type(addr.typ, "address"):
             return LLLnode.from_list(
                 ["extcodehash", addr],
                 typ=BaseType("bytes32"),
                 location=None,
                 pos=getpos(self.expr),
             )
     # self.x: global attribute
     elif isinstance(self.expr.value,
                     vy_ast.Name) and self.expr.value.id == "self":
         var = self.context.globals[self.expr.attr]
         return LLLnode.from_list(
             var.pos,
             typ=var.typ,
             location="storage",
             pos=getpos(self.expr),
             annotation="self." + self.expr.attr,
         )
     # Reserved keywords
     elif (isinstance(self.expr.value, vy_ast.Name)
           and self.expr.value.id in ENVIRONMENT_VARIABLES):
         key = f"{self.expr.value.id}.{self.expr.attr}"
         if key == "msg.sender" and not self.context.is_private:
             return LLLnode.from_list(["caller"],
                                      typ="address",
                                      pos=getpos(self.expr))
         elif key == "msg.value" and self.context.is_payable:
             return LLLnode.from_list(
                 ["callvalue"],
                 typ=BaseType("uint256"),
                 pos=getpos(self.expr),
             )
         elif key == "msg.gas":
             return LLLnode.from_list(
                 ["gas"],
                 typ="uint256",
                 pos=getpos(self.expr),
             )
         elif key == "block.difficulty":
             return LLLnode.from_list(
                 ["difficulty"],
                 typ="uint256",
                 pos=getpos(self.expr),
             )
         elif key == "block.timestamp":
             return LLLnode.from_list(
                 ["timestamp"],
                 typ=BaseType("uint256"),
                 pos=getpos(self.expr),
             )
         elif key == "block.coinbase":
             return LLLnode.from_list(["coinbase"],
                                      typ="address",
                                      pos=getpos(self.expr))
         elif key == "block.number":
             return LLLnode.from_list(["number"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
         elif key == "block.prevhash":
             return LLLnode.from_list(
                 ["blockhash", ["sub", "number", 1]],
                 typ="bytes32",
                 pos=getpos(self.expr),
             )
         elif key == "tx.origin":
             return LLLnode.from_list(["origin"],
                                      typ="address",
                                      pos=getpos(self.expr))
         elif key == "chain.id":
             if not version_check(begin="istanbul"):
                 raise EvmVersionException(
                     "chain.id is unavailable prior to istanbul ruleset",
                     self.expr)
             return LLLnode.from_list(["chainid"],
                                      typ="uint256",
                                      pos=getpos(self.expr))
     # Other variables
     else:
         sub = Expr.parse_variable_location(self.expr.value, self.context)
         # contract type
         if isinstance(sub.typ, InterfaceType):
             return sub
         if isinstance(sub.typ,
                       StructType) and self.expr.attr in sub.typ.members:
             return add_variable_offset(sub,
                                        self.expr.attr,
                                        pos=getpos(self.expr))