Example #1
0
    def get_rhs(self, rhs: Expression, expected_type: AnnotatedTypeName):
        if isinstance(rhs, TupleExpr):
            if not isinstance(rhs, TupleExpr) or not isinstance(
                    expected_type.type_name, TupleType) or len(
                        rhs.elements) != len(expected_type.type_name.types):
                raise TypeMismatchException(expected_type, rhs.annotated_type,
                                            rhs)
            exprs = [
                self.get_rhs(a, e)
                for e, a, in zip(expected_type.type_name.types, rhs.elements)
            ]
            return replace_expr(rhs, TupleExpr(exprs)).as_type(
                TupleType([e.annotated_type for e in exprs]))

        instance = rhs.instanceof(expected_type)
        if not instance:
            raise TypeMismatchException(expected_type, rhs.annotated_type, rhs)
        else:
            if rhs.annotated_type.type_name != expected_type.type_name:
                rhs = self.implicitly_converted_to(rhs,
                                                   expected_type.type_name)

            if instance == 'make-private':
                return self.make_private(rhs, expected_type.privacy_annotation)
            else:
                return rhs
Example #2
0
 def visitIfStatement(self, ast: IfStatement):
     b = ast.condition
     if not b.instanceof_data_type(TypeName.bool_type()):
         raise TypeMismatchException(TypeName.bool_type(),
                                     b.annotated_type.type_name, b)
     if ast.condition.annotated_type.is_private():
         expected = AnnotatedTypeName(TypeName.bool_type(),
                                      Expression.me_expr())
         if not b.instanceof(expected):
             raise TypeMismatchException(expected, b.annotated_type, b)
Example #3
0
    def try_rehom(rhs: Expression, expected_type: AnnotatedTypeName):
        if rhs.annotated_type.is_public():
            raise ValueError(
                'Cannot change the homomorphism of a public value')

        if rhs.annotated_type.is_private_at_me(rhs.analysis):
            # The value is @me, so we can just insert a ReclassifyExpr to change
            # the homomorphism of this value, just like we do for public values.
            return TypeCheckVisitor.make_rehom(rhs, expected_type)

        if isinstance(rhs, ReclassifyExpr) and not isinstance(rhs, RehomExpr):
            # rhs is a valid ReclassifyExpr, i.e. the argument is public or @me-private
            # To create an expression with the correct homomorphism,
            # just change the ReclassifyExpr's output homomorphism
            rhs.homomorphism = expected_type.homomorphism
        elif isinstance(rhs, PrimitiveCastExpr):
            # Ignore primitive cast & recurse
            rhs.expr = TypeCheckVisitor.try_rehom(rhs.expr, expected_type)
        elif isinstance(rhs, FunctionCallExpr) and isinstance(rhs.func, BuiltinFunction) and rhs.func.is_ite() \
                and rhs.args[0].annotated_type.is_public():
            # Argument is public_cond ? true_val : false_val. Try to rehom both true_val and false_val
            rhs.args[1] = TypeCheckVisitor.try_rehom(rhs.args[1],
                                                     expected_type)
            rhs.args[2] = TypeCheckVisitor.try_rehom(rhs.args[2],
                                                     expected_type)
        else:
            raise TypeMismatchException(expected_type, rhs.annotated_type, rhs)

        # Rehom worked without throwing, change annotated_type and return
        rhs.annotated_type = rhs.annotated_type.with_homomorphism(
            expected_type.homomorphism)
        return rhs
Example #4
0
    def handle_homomorphic_builtin_function_call(self, ast: FunctionCallExpr,
                                                 func: BuiltinFunction):
        # First - same as non-homomorphic - check that argument types conform to op signature
        if not func.is_eq():
            for arg, t in zip(ast.args, func.input_types()):
                if not arg.instanceof_data_type(t):
                    raise TypeMismatchException(t,
                                                arg.annotated_type.type_name,
                                                arg)

        homomorphic_func = func.select_homomorphic_overload(
            ast.args, ast.analysis)
        if homomorphic_func is None:
            raise TypeException(
                f'Operation \'{func.op}\' requires all arguments to be accessible, '
                f'i.e. @all or provably equal to @me', ast)

        # We could perform homomorphic operations on-chain by using some Solidity arbitrary precision math library.
        # For now, keep it simple and evaluate homomorphic operations in Python and check the result in the circuit.
        func.is_private = True

        ast.annotated_type = homomorphic_func.output_type()
        func.homomorphism = ast.annotated_type.homomorphism
        expected_arg_types = homomorphic_func.input_types()

        # Check that the argument types are correct
        ast.args[:] = map(lambda arg, arg_pt: self.get_rhs(arg, arg_pt),
                          ast.args, expected_arg_types)
Example #5
0
 def make_private_if_not_already(self, ast: Expression):
     if ast.annotated_type.is_private():
         expected = AnnotatedTypeName(ast.annotated_type.type_name,
                                      Expression.me_expr())
         if not ast.instanceof(expected):
             raise TypeMismatchException(expected, ast.annotated_type, ast)
         return ast
     else:
         return self.make_private(ast, Expression.me_expr())
Example #6
0
 def handle_cast(self, expr: Expression, t: TypeName) -> AnnotatedTypeName:
     # because of the fake solidity check we already know that the cast is possible -> don't have to check if cast possible
     if expr.annotated_type.is_private():
         expected = AnnotatedTypeName(expr.annotated_type.type_name,
                                      Expression.me_expr())
         if not expr.instanceof(expected):
             raise TypeMismatchException(expected, expr.annotated_type,
                                         expr)
         return AnnotatedTypeName(t.clone(), Expression.me_expr())
     else:
         return AnnotatedTypeName(t.clone())
Example #7
0
    def visitIndexExpr(self, ast: IndexExpr):
        arr = ast.arr
        index = ast.key

        map_t = arr.annotated_type
        # should have already been checked
        assert (map_t.privacy_annotation.is_all_expr())

        # do actual type checking
        if isinstance(map_t.type_name, Mapping):
            key_type = map_t.type_name.key_type
            expected = AnnotatedTypeName(key_type, Expression.all_expr())
            instance = index.instanceof(expected)
            if not instance:
                raise TypeMismatchException(expected, index.annotated_type,
                                            ast)

            # record indexing information
            if map_t.type_name.key_label is not None:  # TODO modification correct?
                if index.privacy_annotation_label():
                    map_t.type_name.instantiated_key = index
                else:
                    raise TypeException(
                        f'Index cannot be used as a privacy type for array of type {map_t}',
                        ast)

            # determine value type
            ast.annotated_type = map_t.type_name.value_type

            if not self.is_accessible_by_invoker(ast):
                raise TypeException(
                    "Tried to read value which cannot be proven to be owned by the transaction invoker",
                    ast)
        elif isinstance(map_t.type_name, Array):
            if ast.key.annotated_type.is_private():
                raise TypeException('No private array index', ast)
            if not ast.key.instanceof_data_type(TypeName.number_type()):
                raise TypeException('Array index must be numeric', ast)
            ast.annotated_type = map_t.type_name.value_type
        else:
            raise TypeException('Indexing into non-mapping', ast)
Example #8
0
 def visitForStatement(self, ast: ForStatement):
     if not ast.condition.instanceof(AnnotatedTypeName.bool_all()):
         raise TypeMismatchException(AnnotatedTypeName.bool_all(),
                                     ast.condition.annotated_type,
                                     ast.condition)
Example #9
0
    def handle_builtin_function_call(self, ast: FunctionCallExpr,
                                     func: BuiltinFunction):
        # handle special cases
        if func.is_ite():
            cond_t = ast.args[0].annotated_type

            # Ensure that condition is boolean
            if not cond_t.type_name.implicitly_convertible_to(
                    TypeName.bool_type()):
                raise TypeMismatchException(TypeName.bool_type(),
                                            cond_t.type_name, ast.args[0])

            res_t = ast.args[1].annotated_type.type_name.combined_type(
                ast.args[2].annotated_type.type_name, True)

            if cond_t.is_private():
                # Everything is turned private
                func.is_private = True
                a = res_t.annotate(Expression.me_expr())
            else:
                p = ast.args[1].annotated_type.combined_privacy(
                    ast.analysis, ast.args[2].annotated_type)
                a = res_t.annotate(p)
            ast.args[1] = self.get_rhs(ast.args[1], a)
            ast.args[2] = self.get_rhs(ast.args[2], a)

            ast.annotated_type = a
            return
        elif func.is_parenthesis():
            ast.annotated_type = ast.args[0].annotated_type
            return

        # Check that argument types conform to op signature
        parameter_types = func.input_types()
        if not func.is_eq():
            for arg, t in zip(ast.args, parameter_types):
                if not arg.instanceof_data_type(t):
                    raise TypeMismatchException(t,
                                                arg.annotated_type.type_name,
                                                arg)

        t1 = ast.args[0].annotated_type.type_name
        t2 = None if len(
            ast.args) == 1 else ast.args[1].annotated_type.type_name

        if len(ast.args) == 1:
            arg_t = 'lit' if ast.args[
                0].annotated_type.type_name.is_literal else t1
        else:
            assert len(ast.args) == 2
            is_eq_with_tuples = func.is_eq() and isinstance(t1, TupleType)
            arg_t = t1.combined_type(t2, convert_literals=is_eq_with_tuples)

        # Infer argument and output types
        if arg_t == 'lit':
            res = func.op_func(
                *[arg.annotated_type.type_name.value for arg in ast.args])
            if isinstance(res, bool):
                assert func.output_type() == TypeName.bool_type()
                out_t = BooleanLiteralType(res)
            else:
                assert func.output_type() == TypeName.number_type()
                out_t = NumberLiteralType(res)
            if func.is_eq():
                arg_t = t1.to_abstract_type().combined_type(
                    t2.to_abstract_type(), True)
        elif func.output_type() == TypeName.bool_type():
            out_t = TypeName.bool_type()
        else:
            out_t = arg_t

        assert arg_t is not None and (arg_t != 'lit' or not func.is_eq())

        private_args = any(map(self.has_private_type, ast.args))
        if private_args:
            assert arg_t != 'lit'
            if func.can_be_private():
                if func.is_shiftop():
                    if not ast.args[1].annotated_type.type_name.is_literal:
                        raise TypeException(
                            'Private shift expressions must use a constant (literal) shift amount',
                            ast.args[1])
                    if ast.args[1].annotated_type.type_name.value < 0:
                        raise TypeException('Cannot shift by negative amount',
                                            ast.args[1])
                if func.is_bitop() or func.is_shiftop():
                    for arg in ast.args:
                        if arg.annotated_type.type_name.elem_bitwidth == 256:
                            raise TypeException(
                                'Private bitwise and shift operations are only supported for integer types < 256 bit, '
                                'please use a smaller type', arg)

                if func.is_arithmetic():
                    for a in ast.args:
                        if a.annotated_type.type_name.elem_bitwidth == 256:
                            issue_compiler_warning(
                                func, 'Possible field prime overflow',
                                'Private arithmetic 256bit operations overflow at FIELD_PRIME.\n'
                                'If you need correct overflow behavior, use a smaller integer type.'
                            )
                            break
                elif func.is_comp():
                    for a in ast.args:
                        if a.annotated_type.type_name.elem_bitwidth == 256:
                            issue_compiler_warning(
                                func, 'Possible private comparison failure',
                                'Private 256bit comparison operations will fail for values >= 2^252.\n'
                                'If you cannot guarantee that the value stays in range, you must use '
                                'a smaller integer type to ensure correctness.'
                            )
                            break

                func.is_private = True
                p = Expression.me_expr()
            else:
                raise TypeException(
                    f'Operation \'{func.op}\' does not support private operands',
                    ast)
        else:
            p = None

        if arg_t != 'lit':
            # Add implicit casts for arguments
            arg_pt = arg_t.annotate(p)
            if func.is_shiftop() and p is not None:
                ast.args[0] = self.get_rhs(ast.args[0], arg_pt)
            else:
                ast.args[:] = map(
                    lambda argument: self.get_rhs(argument, arg_pt), ast.args)

        ast.annotated_type = out_t.annotate(p)