Ejemplo n.º 1
0
    def unary_operations(self):
        operand = Expr.parse_value_expr(self.expr.operand, self.context)
        if isinstance(self.expr.op, sri_ast.Not):
            if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool':
                return LLLnode.from_list(["iszero", operand], typ='bool', pos=getpos(self.expr))
            else:
                raise TypeMismatch(
                    f"Only bool is supported for not operation, {operand.typ} supplied.",
                    self.expr,
                )
        elif isinstance(self.expr.op, sri_ast.USub):
            if not is_numeric_type(operand.typ):
                raise TypeMismatch(
                    f"Unsupported type for negation: {operand.typ}",
                    self.expr,
                )

            # Clamp on minimum integer value as we cannot negate that value
            # (all other integer values are fine)
            min_int_val = get_min_val_for_type(operand.typ.typ)
            return LLLnode.from_list(
                    ["sub", 0, ["clampgt", operand, min_int_val]],
                    typ=operand.typ,
                    pos=getpos(self.expr)
                )
        else:
            raise StructureException("Only the 'not' or 'neg' unary operators are supported")
Ejemplo n.º 2
0
 def struct_literals(expr, name, context):
     member_subs = {}
     member_typs = {}
     for key, value in zip(expr.keys, expr.values):
         if not isinstance(key, sri_ast.Name):
             raise TypeMismatch(
                 f"Invalid member variable for struct: {getattr(key, 'id', '')}",
                 key,
             )
         check_valid_varname(
             key.id,
             context.structs,
             context.constants,
             "Invalid member variable for struct",
         )
         if key.id in member_subs:
             raise TypeMismatch("Member variable duplicated: " + key.id, key)
         sub = Expr(value, context).lll_node
         member_subs[key.id] = sub
         member_typs[key.id] = sub.typ
     return LLLnode.from_list(
         ["multi"] + [member_subs[key] for key in member_subs.keys()],
         typ=StructType(member_typs, name, is_literal=True),
         pos=getpos(expr),
     )
Ejemplo n.º 3
0
    def call(self):
        from srilang.functions import (
            DISPATCH_TABLE,
        )

        if isinstance(self.expr.func, sri_ast.Name):
            function_name = self.expr.func.id

            if function_name in DISPATCH_TABLE:
                return DISPATCH_TABLE[function_name].build_LLL(self.expr, self.context)

            # Struct constructors do not need `self` prefix.
            elif function_name in self.context.structs:
                args = self.expr.args
                if len(args) != 1:
                    raise StructureException(
                        "Struct constructor is called with one argument only",
                        self.expr,
                    )

                arg = args[0]
                if not isinstance(arg, sri_ast.Dict):
                    raise TypeMismatch(
                        "Struct can only be constructed with a dict",
                        self.expr,
                    )
                return Expr.struct_literals(arg, function_name, self.context)

            # Contract assignment. Bar(<address>).
            elif function_name in self.context.sigs:
                ret, arg_lll = self._is_valid_contract_assign()
                if ret is True:
                    arg_lll.typ = ContractType(function_name)  # Cast to Correct contract type.
                    return arg_lll
                else:
                    raise TypeMismatch(
                        "ContractType definition expects one address argument.",
                        self.expr,
                    )
            else:
                err_msg = f"Not a top-level function: {function_name}"
                if function_name in [x.split('(')[0] for x, _ in self.context.sigs['self'].items()]:
                    err_msg += f". Did you mean self.{function_name}?"
                raise StructureException(err_msg, self.expr)
        elif isinstance(self.expr.func, sri_ast.Attribute) and isinstance(self.expr.func.value, sri_ast.Name) and self.expr.func.value.id == "self":  # noqa: E501
            return self_call.make_call(self.expr, self.context)
        else:
            return external_call.make_external_call(self.expr, self.context)
Ejemplo n.º 4
0
    def list_literals(self):

        if not len(self.expr.elts):
            raise StructureException("List must have elements", self.expr)

        def get_out_type(lll_node):
            if isinstance(lll_node, ListType):
                return get_out_type(lll_node.subtype)
            return lll_node.typ

        o = []
        previous_type = None
        out_type = None

        for elt in self.expr.elts:
            current_lll_node = Expr(elt, self.context).lll_node
            if not out_type:
                out_type = current_lll_node.typ

            current_type = get_out_type(current_lll_node)
            if len(o) > 0 and previous_type != current_type:
                raise TypeMismatch("Lists may only contain one type", self.expr)
            else:
                o.append(current_lll_node)
                previous_type = current_type

        return LLLnode.from_list(
            ["multi"] + o,
            typ=ListType(out_type, len(o)),
            pos=getpos(self.expr),
        )
Ejemplo n.º 5
0
def get_min_val_for_type(typ: str) -> int:
    key = 'MIN_' + typ.upper()
    try:
        min_val, _ = BUILTIN_CONSTANTS[key]
    except KeyError as e:
        raise TypeMismatch(f"Not a signed type: {typ}") from e
    return min_val
Ejemplo n.º 6
0
 def _check_implicit_conversion(self, var_id, sub):
     target_typ = self.context.vars[var_id].typ
     assign_typ = sub.typ
     if isinstance(target_typ, BaseType) and isinstance(
             assign_typ, BaseType):
         if not assign_typ.is_literal and assign_typ.typ != target_typ.typ:
             raise TypeMismatch(
                 f'Invalid type {assign_typ.typ}, expected: {target_typ.typ}',
                 self.stmt)
Ejemplo n.º 7
0
 def subscript(self):
     sub = Expr.parse_variable_location(self.expr.value, self.context)
     if isinstance(sub.typ, (MappingType, ListType)):
         if not isinstance(self.expr.slice, sri_ast.Index):
             raise StructureException(
                 "Array access must access a single element, not a slice",
                 self.expr,
             )
         index = Expr.parse_value_expr(self.expr.slice.value, self.context)
     elif isinstance(sub.typ, TupleType):
         if not isinstance(self.expr.slice.value, sri_ast.Int) or self.expr.slice.value.n < 0 or self.expr.slice.value.n >= len(sub.typ.members):  # noqa: E501
             raise TypeMismatch("Tuple index invalid", self.expr.slice.value)
         index = self.expr.slice.value.n
     else:
         raise TypeMismatch("Bad subscript attempt", self.expr.value)
     o = add_variable_offset(sub, index, pos=getpos(self.expr))
     o.mutable = sub.mutable
     return o
Ejemplo n.º 8
0
def base_type_conversion(orig, frm, to, pos, in_function_call=False):
    orig = unwrap_location(orig)

    # do the base type check so we can use BaseType attributes
    if not isinstance(frm, BaseType) or not isinstance(to, BaseType):
        raise TypeMismatch(
            f"Base type conversion from or to non-base type: {frm} {to}", pos)

    if getattr(frm, 'is_literal', False):
        if frm.typ in ('int128', 'uint256'):
            if not SizeLimits.in_bounds(frm.typ, orig.value):
                raise InvalidLiteral(f"Number out of range: {orig.value}", pos)

        if to.typ in ('int128', 'uint256'):
            if not SizeLimits.in_bounds(to.typ, orig.value):
                raise InvalidLiteral(f"Number out of range: {orig.value}", pos)

    is_decimal_int128_conversion = frm.typ == 'int128' and to.typ == 'decimal'
    is_same_type = frm.typ == to.typ
    is_literal_conversion = frm.is_literal and (frm.typ, to.typ) == ('int128',
                                                                     'uint256')
    is_address_conversion = isinstance(frm,
                                       ContractType) and to.typ == 'address'
    if not (is_same_type or is_literal_conversion or is_address_conversion
            or is_decimal_int128_conversion):
        raise TypeMismatch(
            f"Typecasting from base type {frm} to {to} unavailable", pos)

    # handle None value inserted by `empty()`
    if orig.value is None:
        return LLLnode.from_list(0, typ=to)

    if is_decimal_int128_conversion:
        return LLLnode.from_list(
            ['mul', orig, DECIMAL_DIVISOR],
            typ=BaseType('decimal'),
        )

    return LLLnode(orig.value,
                   orig.args,
                   typ=to,
                   add_gas_estimate=orig.add_gas_estimate)
Ejemplo n.º 9
0
def _to_bytelike(expr, args, kwargs, context, bytetype):
    if bytetype == 'string':
        ReturnType = StringType
    elif bytetype == 'bytes':
        ReturnType = ByteArrayType
    else:
        raise TypeMismatch(f'Invalid {bytetype} supplied')

    in_arg = args[0]
    if in_arg.typ.maxlen > args[1].slice.value.n:
        raise TypeMismatch(
            f'Cannot convert as input {bytetype} are larger than max length',
            expr,
        )

    return LLLnode(value=in_arg.value,
                   args=in_arg.args,
                   typ=ReturnType(in_arg.typ.maxlen),
                   pos=getpos(expr),
                   location=in_arg.location)
Ejemplo n.º 10
0
def pack_logging_topics(event_id, args, expected_topics, context, pos):
    topics = [event_id]
    code_pos = pos
    for pos, expected_topic in enumerate(expected_topics):
        expected_type = expected_topic.typ
        arg = args[pos]
        value = Expr(arg, context).lll_node
        arg_type = value.typ

        if isinstance(arg_type, ByteArrayLike) and isinstance(expected_type, ByteArrayLike):
            if arg_type.maxlen > expected_type.maxlen:
                raise TypeMismatch(
                    f"Topic input bytes are too big: {arg_type} {expected_type}", code_pos
                )
            if isinstance(arg, sri_ast.Str):
                bytez, bytez_length = string_to_bytes(arg.s)
                if len(bytez) > 32:
                    raise InvalidLiteral(
                        "Can only log a maximum of 32 bytes at a time.", code_pos
                    )
                topics.append(bytes_to_int(bytez + b'\x00' * (32 - bytez_length)))
            else:
                if value.location == "memory":
                    size = ['mload', value]
                elif value.location == "storage":
                    size = ['sload', ['sha3_32', value]]
                topics.append(byte_array_to_num(value, arg, 'uint256', size))
        else:
            if arg_type != expected_type:
                raise TypeMismatch(
                    f"Invalid type for logging topic, got {arg_type} expected {expected_type}",
                    value.pos
                )
            value = unwrap_location(value)
            value = base_type_conversion(value, arg_type, expected_type, pos=code_pos)
            topics.append(value)

    return topics
Ejemplo n.º 11
0
 def _check_valid_assign(self, sub):
     if (isinstance(self.stmt.annotation, sri_ast.Name)
             and self.stmt.annotation.id == 'bytes32'):
         # CMC 04/07/2020 this check could be much clearer, more like:
         # if isinstance(ByteArrayLike) and maxlen == 32
         #   or isinstance(BaseType) and typ.typ == 'bytes32'
         #   then GOOD
         #   else RAISE
         if isinstance(sub.typ, ByteArrayLike):
             if sub.typ.maxlen != 32:
                 raise TypeMismatch(
                     'Invalid type, expected: bytes32. String is incorrect length.',
                     self.stmt)
             return
         elif isinstance(sub.typ, BaseType):
             if sub.typ.typ != 'bytes32':
                 raise TypeMismatch('Invalid type, expected: bytes32',
                                    self.stmt)
             return
         else:
             raise TypeMismatch("Invalid type, expected: bytes32",
                                self.stmt)
     elif isinstance(self.stmt.annotation, sri_ast.Subscript):
         # check list assign:
         if not isinstance(sub.typ, (ListType, ByteArrayLike)):
             raise TypeMismatch(
                 f'Invalid type, expected: {self.stmt.annotation.value.id},'
                 f' got: {sub.typ}', self.stmt)
     elif sub.typ is None:
         # Check that the object to be assigned is not of NoneType
         raise TypeMismatch(
             f"Invalid type, expected {self.stmt.annotation.id}", self.stmt)
     elif isinstance(sub.typ, StructType):
         # This needs to get more sophisticated in the presence of
         # foreign structs.
         if not sub.typ.name == self.stmt.annotation.id:
             raise TypeMismatch(
                 f"Invalid type, expected {self.stmt.annotation.id}",
                 self.stmt)
     # Check that the integer literal, can be assigned to uint256 if necessary.
     elif (self.stmt.annotation.id,
           sub.typ.typ) == ('uint256', 'int128') and sub.typ.is_literal:
         if not SizeLimits.in_bounds('uint256', sub.value):
             raise InvalidLiteral(
                 'Invalid uint256 assignment, value not in uint256 range.',
                 self.stmt)
     elif self.stmt.annotation.id != sub.typ.typ:
         raise TypeMismatch(
             f'Invalid type {sub.typ.typ}, expected: {self.stmt.annotation.id}',
             self.stmt,
         )
     else:
         return True
Ejemplo n.º 12
0
def get_external_contract_keywords(stmt_expr, context):
    from srilang.parser.expr import Expr
    value, gas = None, None
    for kw in stmt_expr.keywords:
        if kw.arg not in ('value', 'gas'):
            raise TypeMismatch(
                'Invalid keyword argument, only "gas" and "value" supported.',
                stmt_expr,
            )
        elif kw.arg == 'gas':
            gas = Expr.parse_value_expr(kw.value, context)
        elif kw.arg == 'value':
            value = Expr.parse_value_expr(kw.value, context)
    return value, gas
Ejemplo n.º 13
0
    def parse_assert(self):

        with self.context.assertion_scope():
            test_expr = Expr.parse_value_expr(self.stmt.test, self.context)

        if not self.is_bool_expr(test_expr):
            raise TypeMismatch('Only boolean expressions allowed',
                               self.stmt.test)
        if self.stmt.msg:
            return self._assert_reason(test_expr, self.stmt.msg)
        else:
            return LLLnode.from_list(['assert', test_expr],
                                     typ=None,
                                     pos=getpos(self.stmt))
Ejemplo n.º 14
0
    def boolean_operations(self):
        # Iterate through values
        for value in self.expr.values:
            # Check for calls at assignment
            if self.context.in_assignment and isinstance(value, sri_ast.Call):
                raise StructureException(
                    "Boolean operations with calls may not be performed on assignment",
                    self.expr,
                )

            # Check for boolean operations with non-boolean inputs
            _expr = Expr.parse_value_expr(value, self.context)
            if not is_base_type(_expr.typ, 'bool'):
                raise TypeMismatch(
                    "Boolean operations can only be between booleans!",
                    self.expr,
                )

            # TODO: Handle special case of literals and simplify at compile time

        # Check for valid ops
        if isinstance(self.expr.op, sri_ast.And):
            op = 'and'
        elif isinstance(self.expr.op, sri_ast.Or):
            op = 'or'
        else:
            raise Exception("Unsupported bool op: " + self.expr.op)

        # Handle different numbers of inputs
        count = len(self.expr.values)
        if count < 2:
            raise StructureException("Expected at least two arguments for a bool op", self.expr)
        elif count == 2:
            left = Expr.parse_value_expr(self.expr.values[0], self.context)
            right = Expr.parse_value_expr(self.expr.values[1], self.context)
            return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))
        else:
            left = Expr.parse_value_expr(self.expr.values[0], self.context)
            right = Expr.parse_value_expr(self.expr.values[1], self.context)

            p = ['seq', [op, left, right]]
            values = self.expr.values[2:]
            while len(values) > 0:
                value = Expr.parse_value_expr(values[0], self.context)
                p = [op, value, p]
                values = values[1:]

            return LLLnode.from_list(p, typ='bool', pos=getpos(self.expr))
Ejemplo n.º 15
0
def get_external_contract_call_output(sig, context):
    if not sig.output_type:
        return 0, 0, []
    output_placeholder = context.new_placeholder(typ=sig.output_type)
    output_size = get_size_of_type(sig.output_type) * 32
    if isinstance(sig.output_type, BaseType):
        returner = [0, output_placeholder]
    elif isinstance(sig.output_type, ByteArrayLike):
        returner = [0, output_placeholder + 32]
    elif isinstance(sig.output_type, TupleLike):
        returner = [0, output_placeholder]
    elif isinstance(sig.output_type, ListType):
        returner = [0, output_placeholder]
    else:
        raise TypeMismatch(f"Invalid output type: {sig.output_type}")
    return output_placeholder, output_size, returner
Ejemplo n.º 16
0
    def unroll_constant(self, const, global_ctx):
        ann_expr = None
        expr = Expr.parse_value_expr(
            const.value,
            Context(vars=None,
                    global_ctx=global_ctx,
                    origcode=const.full_source_code,
                    memory_allocator=MemoryAllocator()),
        )
        annotation_type = global_ctx.parse_type(const.annotation.args[0], None)
        fail = False

        if is_instances([expr.typ, annotation_type], ByteArrayType):
            if expr.typ.maxlen < annotation_type.maxlen:
                return const
            fail = True

        elif expr.typ != annotation_type:
            fail = True
            # special case for literals, which can be uint256 types as well.
            is_special_case_uint256_literal = (is_instances(
                [expr.typ, annotation_type], BaseType)) and ([
                    annotation_type.typ, expr.typ.typ
                ] == ['uint256', 'int128']) and SizeLimits.in_bounds(
                    'uint256', expr.value)

            is_special_case_int256_literal = (is_instances(
                [expr.typ, annotation_type], BaseType)) and ([
                    annotation_type.typ, expr.typ.typ
                ] == ['int128', 'int128']) and SizeLimits.in_bounds(
                    'int128', expr.value)

            if is_special_case_uint256_literal or is_special_case_int256_literal:
                fail = False

        if fail:
            raise TypeMismatch(
                f"Invalid value for constant type, expected {annotation_type} got "
                f"{expr.typ} instead",
                const.value,
            )

        ann_expr = copy.deepcopy(expr)
        ann_expr.typ = annotation_type
        ann_expr.typ.is_literal = expr.typ.is_literal  # Annotation type doesn't have literal set.

        return ann_expr
Ejemplo n.º 17
0
    def ann_assign(self):
        with self.context.assignment_scope():
            typ = parse_type(
                self.stmt.annotation,
                location='memory',
                custom_structs=self.context.structs,
                constants=self.context.constants,
            )
            if isinstance(self.stmt.target, sri_ast.Attribute):
                raise TypeMismatch(
                    f'May not set type for field {self.stmt.target.attr}',
                    self.stmt,
                )
            varname = self.stmt.target.id
            pos = self.context.new_variable(varname, typ)
            if self.stmt.value is None:
                raise StructureException(
                    'New variables must be initialized explicitly', self.stmt)

            sub = Expr(self.stmt.value, self.context).lll_node

            is_literal_bytes32_assign = (isinstance(sub.typ, ByteArrayType)
                                         and sub.typ.maxlen == 32
                                         and isinstance(typ, BaseType)
                                         and typ.typ == 'bytes32'
                                         and sub.typ.is_literal)

            # If bytes[32] to bytes32 assignment rewrite sub as bytes32.
            if is_literal_bytes32_assign:
                sub = LLLnode(
                    bytes_to_int(self.stmt.value.s),
                    typ=BaseType('bytes32'),
                    pos=getpos(self.stmt),
                )

            self._check_valid_assign(sub)
            self._check_same_variable_assign(sub)
            variable_loc = LLLnode.from_list(
                pos,
                typ=typ,
                location='memory',
                pos=getpos(self.stmt),
            )
            o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt))

            return o
Ejemplo n.º 18
0
    def parse_if(self):
        if self.stmt.orelse:
            block_scope_id = id(self.stmt.orelse)
            with self.context.make_blockscope(block_scope_id):
                add_on = [parse_body(self.stmt.orelse, self.context)]
        else:
            add_on = []

        block_scope_id = id(self.stmt)
        with self.context.make_blockscope(block_scope_id):
            test_expr = Expr.parse_value_expr(self.stmt.test, self.context)

            if not self.is_bool_expr(test_expr):
                raise TypeMismatch('Only boolean expressions allowed',
                                   self.stmt.test)
            body = ['if', test_expr,
                    parse_body(self.stmt.body, self.context)] + add_on
            o = LLLnode.from_list(body, typ=None, pos=getpos(self.stmt))
        return o
Ejemplo n.º 19
0
    def assign(self):
        # Assignment (e.g. x[4] = y)

        with self.context.assignment_scope():
            sub = Expr(self.stmt.value, self.context).lll_node

            # Error check when assigning to declared variable
            if isinstance(self.stmt.target, sri_ast.Name):
                # Do not allow assignment to undefined variables without annotation
                if self.stmt.target.id not in self.context.vars:
                    raise VariableDeclarationException(
                        "Variable type not defined", self.stmt)

                # Check against implicit conversion
                self._check_implicit_conversion(self.stmt.target.id, sub)

            is_valid_tuple_assign = (isinstance(
                self.stmt.target, sri_ast.Tuple)) and isinstance(
                    self.stmt.value, sri_ast.Tuple)

            # Do no allow tuple-to-tuple assignment
            if is_valid_tuple_assign:
                raise VariableDeclarationException(
                    "Tuple to tuple assignment not supported",
                    self.stmt,
                )

            # Checks to see if assignment is valid
            target = self.get_target(self.stmt.target)
            if isinstance(target.typ, ContractType) and not isinstance(
                    sub.typ, ContractType):
                raise TypeMismatch(
                    'Contract assignment expects casted address: '
                    f'{target.typ}(<address_var>)', self.stmt)
            o = make_setter(target,
                            sub,
                            target.location,
                            pos=getpos(self.stmt))

            o.pos = getpos(self.stmt)

        return o
Ejemplo n.º 20
0
def to_bool(expr, args, kwargs, context):
    in_arg = args[0]
    input_type, _ = get_type(in_arg)

    if input_type == 'bytes':
        if in_arg.typ.maxlen > 32:
            raise TypeMismatch(
                f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to bool",
                expr,
            )
        else:
            num = byte_array_to_num(in_arg, expr, 'uint256')
            return LLLnode.from_list(['iszero', ['iszero', num]],
                                     typ=BaseType('bool'),
                                     pos=getpos(expr))

    else:
        return LLLnode.from_list(['iszero', ['iszero', in_arg]],
                                 typ=BaseType('bool'),
                                 pos=getpos(expr))
Ejemplo n.º 21
0
def call_lookup_specs(stmt_expr, context):
    from srilang.parser.expr import Expr

    method_name = stmt_expr.func.attr

    if len(stmt_expr.keywords):
        raise TypeMismatch(
            "Cannot use keyword arguments in calls to functions via 'self'",
            stmt_expr,
        )
    expr_args = [Expr(arg, context).lll_node for arg in stmt_expr.args]

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

    return method_name, expr_args, sig
Ejemplo n.º 22
0
    def evaluate(self) -> srilangNode:
        """
        Attempt to evaluate the comparison.

        Returns
        -------
        NameConstant
            Node representing the result of the evaluation.
        """
        left, right = self.left, self.right
        if not isinstance(left, Constant):
            raise UnfoldableNode(
                "Node contains invalid field(s) for evaluation")

        if isinstance(self.op, In):
            if not isinstance(right, List):
                raise UnfoldableNode(
                    "Node contains invalid field(s) for evaluation")
            if next((i for i in right.elts if not isinstance(i, Constant)),
                    None):
                raise UnfoldableNode(
                    "Node contains invalid field(s) for evaluation")
            if len(set([type(i) for i in right.elts])) > 1:
                raise UnfoldableNode("List contains multiple literal types")
            value = self.op._op(left.value, [i.value for i in right.elts])
            return NameConstant.from_node(self, value=value)

        if not isinstance(left, type(right)):
            raise UnfoldableNode("Cannot compare different literal types")

        if not isinstance(self.op, (Eq, NotEq)) and not isinstance(
                left, (Int, Decimal)):
            raise TypeMismatch(
                f"Invalid literal types for {self.op.description} comparison",
                self)

        value = self.op._op(left.value, right.value)
        return NameConstant.from_node(self, value=value)
Ejemplo n.º 23
0
def to_bytes32(expr, args, kwargs, context):
    in_arg = args[0]
    input_type, _len = get_type(in_arg)

    if input_type == 'bytes':
        if _len > 32:
            raise TypeMismatch(
                f"Unable to convert bytes[{_len}] to bytes32, max length is too "
                "large.")

        if in_arg.location == "memory":
            return LLLnode.from_list(['mload', ['add', in_arg, 32]],
                                     typ=BaseType('bytes32'))
        elif in_arg.location == "storage":
            return LLLnode.from_list(
                ['sload', ['add', ['sha3_32', in_arg], 1]],
                typ=BaseType('bytes32'))

    else:
        return LLLnode(value=in_arg.value,
                       args=in_arg.args,
                       typ=BaseType('bytes32'),
                       pos=getpos(expr))
Ejemplo n.º 24
0
def make_byte_array_copier(destination, source, pos=None):
    if not isinstance(source.typ, ByteArrayLike):
        btype = 'byte array' if isinstance(destination.typ,
                                           ByteArrayType) else 'string'
        raise TypeMismatch(f"Can only set a {btype} to another {btype}", pos)
    if isinstance(
            source.typ,
            ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen:
        raise TypeMismatch(
            f"Cannot cast from greater max-length {source.typ.maxlen} to shorter "
            f"max-length {destination.typ.maxlen}")

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

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

    if source.value is None:
        pos_node = source
    else:
        pos_node = LLLnode.from_list('_pos',
                                     typ=source.typ,
                                     location=source.location)
    # Get the length
    if source.value is None:
        length = 1
    elif source.location == "memory":
        length = ['add', ['mload', '_pos'], 32]
    elif source.location == "storage":
        length = ['add', ['sload', '_pos'], 32]
        pos_node = LLLnode.from_list(
            ['sha3_32', pos_node],
            typ=source.typ,
            location=source.location,
        )
    else:
        raise CompilerPanic(f"Unsupported location: {source.location}")
    if destination.location == "storage":
        destination = LLLnode.from_list(
            ['sha3_32', destination],
            typ=destination.typ,
            location=destination.location,
        )
    # Maximum theoretical length
    max_length = 32 if source.value is None else source.typ.maxlen + 32
    return LLLnode.from_list([
        'with', '_pos', 0 if source.value is None else source,
        make_byte_slice_copier(
            destination, pos_node, length, max_length, pos=pos)
    ],
                             typ=None)
Ejemplo n.º 25
0
def make_setter(left, right, location, pos, in_function_call=False):
    # Basic types
    if isinstance(left.typ, BaseType):
        right = base_type_conversion(
            right,
            right.typ,
            left.typ,
            pos,
            in_function_call=in_function_call,
        )
        if location == 'storage':
            return LLLnode.from_list(['sstore', left, right], typ=None)
        elif location == 'memory':
            return LLLnode.from_list(['mstore', left, right], typ=None)
    # Byte arrays
    elif isinstance(left.typ, ByteArrayLike):
        return make_byte_array_copier(left, right, pos)
    # Can't copy mappings
    elif isinstance(left.typ, MappingType):
        raise TypeMismatch(
            "Cannot copy mappings; can only copy individual elements", pos)
    # Arrays
    elif isinstance(left.typ, ListType):
        # Cannot do something like [a, b, c] = [1, 2, 3]
        if left.value == "multi":
            raise Exception("Target of set statement must be a single item")

        if not isinstance(right.typ, ListType):
            raise TypeMismatch(
                f"Setter type mismatch: left side is {left.typ}, right side is {right.typ}",
                pos)
        if right.typ.count != left.typ.count:
            raise TypeMismatch("Mismatched number of elements", 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 the right side is a literal
        if right.value == "multi":
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                right.args[i],
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        elif right.value is None:
            if right.typ != left.typ:
                raise TypeMismatch(
                    f"left side is {left.typ}, right side is {right.typ}", pos)
            if left.location == 'memory':
                return mzero(left, 32 * get_size_of_type(left.typ))

            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                LLLnode.from_list(None, typ=right.typ.subtype),
                                location,
                                pos=pos))
            return LLLnode.from_list(['with', '_L', left, ['seq'] + subs],
                                     typ=None)
        # If the right side is a variable
        else:
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            subs = []
            for i in range(left.typ.count):
                subs.append(
                    make_setter(add_variable_offset(
                        left_token,
                        LLLnode.from_list(i, typ='int128'),
                        pos=pos,
                        array_bounds_check=False,
                    ),
                                add_variable_offset(
                                    right_token,
                                    LLLnode.from_list(i, typ='int128'),
                                    pos=pos,
                                    array_bounds_check=False,
                                ),
                                location,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None)
    # Structs
    elif isinstance(left.typ, TupleLike):
        if left.value == "multi" and isinstance(left.typ, StructType):
            raise Exception("Target of set statement must be a single item")
        if right.value is not None:
            if not isinstance(right.typ, left.typ.__class__):
                raise TypeMismatch(
                    f"Setter type mismatch: left side is {left.typ}, right side is {right.typ}",
                    pos,
                )
            if isinstance(left.typ, StructType):
                for k in left.typ.members:
                    if k not in right.typ.members:
                        raise TypeMismatch(
                            f"Keys don't match for structs, missing {k}",
                            pos,
                        )
                for k in right.typ.members:
                    if k not in left.typ.members:
                        raise TypeMismatch(
                            f"Keys don't match for structs, extra {k}",
                            pos,
                        )
                if left.typ.name != right.typ.name:
                    raise TypeMismatch(f"Expected {left.typ}, got {right.typ}",
                                       pos)
            else:
                if len(left.typ.members) != len(right.typ.members):
                    raise TypeMismatch(
                        "Tuple lengths don't match, "
                        f"{len(left.typ.members)} vs {len(right.typ.members)}",
                        pos,
                    )

        left_token = LLLnode.from_list('_L',
                                       typ=left.typ,
                                       location=left.location)
        if left.location == "storage":
            left = LLLnode.from_list(['sha3_32', left],
                                     typ=left.typ,
                                     location="storage_prehashed")
            left_token.location = "storage_prehashed"
        keyz = left.typ.tuple_keys()

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

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

            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 = []
            static_offset_counter = 0
            zipped_components = zip(left.args, right.typ.members, locations)
            for var_arg in left.args:
                if var_arg.location == 'calldata':
                    raise ConstancyViolation(
                        f"Cannot modify function argument: {var_arg.annotation}",
                        pos)
            for left_arg, right_arg, loc in zipped_components:
                if isinstance(right_arg, ByteArrayLike):
                    RType = ByteArrayType if isinstance(
                        right_arg, ByteArrayType) else StringType
                    offset = LLLnode.from_list([
                        'add', '_R',
                        ['mload', ['add', '_R', static_offset_counter]]
                    ],
                                               typ=RType(right_arg.maxlen),
                                               location='memory',
                                               pos=pos)
                    static_offset_counter += 32
                else:
                    offset = LLLnode.from_list(
                        ['mload', ['add', '_R', static_offset_counter]],
                        typ=right_arg.typ,
                        pos=pos,
                    )
                    static_offset_counter += get_size_of_type(right_arg) * 32
                subs.append(make_setter(left_arg, offset, loc, pos=pos))
            return LLLnode.from_list(
                ['with', '_R', right, ['seq'] + subs],
                typ=None,
                annotation='Tuple assignment',
            )
        # If the right side is a variable
        else:
            subs = []
            right_token = LLLnode.from_list('_R',
                                            typ=right.typ,
                                            location=right.location)
            for typ, loc in zip(keyz, locations):
                subs.append(
                    make_setter(add_variable_offset(left_token, typ, pos=pos),
                                add_variable_offset(right_token, typ, pos=pos),
                                loc,
                                pos=pos))
            return LLLnode.from_list(
                ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]],
                typ=None,
            )
    else:
        raise Exception("Invalid type for setters")
Ejemplo n.º 26
0
def pack_arguments(signature,
                   args,
                   context,
                   stmt_expr,
                   return_placeholder=True):
    pos = getpos(stmt_expr)
    placeholder_typ = ByteArrayType(
        maxlen=sum([get_size_of_type(arg.typ)
                    for arg in signature.args]) * 32 + 32)
    placeholder = context.new_placeholder(placeholder_typ)
    setters = [['mstore', placeholder, signature.method_id]]
    needpos = False
    staticarray_offset = 0
    expected_arg_count = len(signature.args)
    actual_arg_count = len(args)
    if actual_arg_count != expected_arg_count:
        raise StructureException(
            f"Wrong number of args for: {signature.name} "
            f"({actual_arg_count} args given, expected {expected_arg_count}",
            stmt_expr)

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

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

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

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

    # For private call usage, doesn't use a returner.
    returner = [[placeholder + 28]] if return_placeholder else []
    if needpos:
        return (LLLnode.from_list([
            'with', '_poz',
            len(args) * 32 + staticarray_offset, ['seq'] + setters + returner
        ],
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
    else:
        return (LLLnode.from_list(['seq'] + setters + returner,
                                  typ=placeholder_typ,
                                  location='memory'),
                placeholder_typ.maxlen - 28, placeholder + 32)
Ejemplo n.º 27
0
def add_variable_offset(parent, key, pos, array_bounds_check=True):
    typ, location = parent.typ, parent.location
    if isinstance(typ, TupleLike):
        if isinstance(typ, StructType):
            if not isinstance(key, str):
                raise TypeMismatch(
                    f"Expecting a member variable access; cannot access element {key}",
                    pos)
            if key not in typ.members:
                raise TypeMismatch(
                    f"Object does not have member variable {key}", pos)
            subtype = typ.members[key]
            attrs = list(typ.tuple_keys())

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

        if location == 'storage':
            return LLLnode.from_list(
                [
                    'add', ['sha3_32', parent],
                    LLLnode.from_list(index, annotation=annotation)
                ],
                typ=subtype,
                location='storage',
            )
        elif location == 'storage_prehashed':
            return LLLnode.from_list(
                [
                    'add', parent,
                    LLLnode.from_list(index, annotation=annotation)
                ],
                typ=subtype,
                location='storage',
            )
        elif location in ('calldata', 'memory'):
            offset = 0
            for i in range(index):
                offset += 32 * get_size_of_type(typ.members[attrs[i]])
            return LLLnode.from_list(['add', offset, parent],
                                     typ=typ.members[key],
                                     location=location,
                                     annotation=annotation)
        else:
            raise TypeMismatch("Not expecting a member variable access", pos)

    elif isinstance(typ, MappingType):

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

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

    elif isinstance(typ, ListType):

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

        if not array_bounds_check:
            sub = k
        elif key.typ.is_literal:  # note: BaseType always has is_literal attr
            # perform the check at compile time and elide the runtime check.
            if key.value < 0 or key.value >= typ.count:
                raise ArrayIndexException(
                    'Array index determined to be out of bounds. '
                    f'Index is {key.value} but array size is {typ.count}', pos)
            sub = k
        else:
            # this works, even for int128. for int128, since two's-complement
            # is used, if the index is negative, (unsigned) LT will interpret
            # it as a very large number, larger than any practical value for
            # an array index, and the clamp will throw an error.
            sub = ['uclamplt', k, typ.count]

        if location == 'storage':
            return LLLnode.from_list(['add', ['sha3_32', parent], sub],
                                     typ=subtype,
                                     location='storage',
                                     pos=pos)
        elif location == 'storage_prehashed':
            return LLLnode.from_list(['add', parent, sub],
                                     typ=subtype,
                                     location='storage',
                                     pos=pos)
        elif location in ('calldata', 'memory'):
            offset = 32 * get_size_of_type(subtype)
            return LLLnode.from_list(['add', ['mul', offset, sub], parent],
                                     typ=subtype,
                                     location=location,
                                     pos=pos)
        else:
            raise TypeMismatch("Not expecting an array access ", pos)
    else:
        raise TypeMismatch(
            f"Cannot access the child of a constant variable! {typ}", pos)
Ejemplo n.º 28
0
 def check_list_type_match(provided):  # Check list types match.
     if provided != typ:
         raise TypeMismatch(
             f"Log list type '{provided}' does not match provided, expected '{typ}'"
         )
Ejemplo n.º 29
0
def pack_logging_data(expected_data, args, context, pos):
    # Checks to see if there's any data
    if not args:
        return ['seq'], 0, None, 0
    holder = ['seq']
    maxlen = len(args) * 32  # total size of all packed args (upper limit)

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

        if isinstance(arg, (sri_ast.Str, sri_ast.Call)):
            expr = Expr(arg, context)
            source_lll = expr.lll_node
            typ = source_lll.typ

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

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

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

    requires_dynamic_offset = any([isinstance(data.typ, ByteArrayLike) for data in expected_data])
    if requires_dynamic_offset:
        dynamic_offset_counter = context.new_placeholder(BaseType(32))
        dynamic_placeholder = context.new_placeholder(BaseType(32))
    else:
        dynamic_offset_counter = None

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

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

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

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

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

    # Copy necessary data into allocated dynamic section.
    for i, (arg, data) in enumerate(zip(args, expected_data)):
        typ = data.typ
        if isinstance(typ, ByteArrayLike):
            pack_args_by_32(
                holder=holder,
                maxlen=maxlen,
                arg=prealloacted.get(i, arg),
                typ=typ,
                context=context,
                placeholder=placeholder_map[i],
                datamem_start=datamem_start,
                dynamic_offset_counter=dynamic_offset_counter,
                pos=pos
            )

    return holder, maxlen, dynamic_offset_counter, datamem_start
Ejemplo n.º 30
0
 def _op(self, left, right):
     if isinstance(left, decimal.Decimal):
         raise TypeMismatch(
             "Cannot perform exponentiation on decimal values.",
             self._parent)
     return int(left**right)