Esempio n. 1
0
    def visitAnnotatedTypeName(self, ast: AnnotatedTypeName):
        if type(ast.type_name) == UserDefinedTypeName:
            if not isinstance(ast.type_name.target, EnumDefinition):
                raise TypeException('Unsupported use of user-defined type',
                                    ast.type_name)
            ast.type_name = ast.type_name.target.annotated_type.type_name.clone(
            )

        if ast.privacy_annotation != Expression.all_expr():
            if not ast.type_name.can_be_private():
                raise TypeException(
                    f'Currently, we do not support private {str(ast.type_name)}',
                    ast)

        p = ast.privacy_annotation
        if isinstance(p, IdentifierExpr):
            t = p.target
            if isinstance(t, Mapping):
                # no action necessary, this is the case: mapping(address!x => uint@x)
                pass
            elif not t.is_final and not t.is_constant:
                raise TypeException(
                    'Privacy annotations must be "final" or "constant", if they are expressions',
                    p)
            elif t.annotated_type != AnnotatedTypeName.address_all():
                raise TypeException(
                    f'Privacy type is not a public address, but {str(t.annotated_type)}',
                    p)
Esempio n. 2
0
 def visitFunctionCallExpr(self, ast: FunctionCallExpr):
     can_be_private = True
     has_nonstatic_call = False
     if ast.evaluate_privately and not ast.is_cast:
         if isinstance(ast.func, LocationExpr):
             assert ast.func.target is not None
             assert isinstance(ast.func.target.annotated_type.type_name,
                               FunctionTypeName)
             has_nonstatic_call |= not ast.func.target.has_static_body
             can_be_private &= ast.func.target.can_be_private
         elif isinstance(ast.func, BuiltinFunction):
             can_be_private &= (ast.func.can_be_private()
                                or ast.annotated_type.type_name.is_literal)
             if ast.func.is_eq() or ast.func.is_ite():
                 can_be_private &= ast.args[
                     1].annotated_type.type_name.can_be_private()
     if has_nonstatic_call:
         raise TypeException(
             'Function calls to non static functions are not allowed inside private expressions',
             ast)
     if not can_be_private:
         raise TypeException(
             'Calls to functions with operations which cannot be expressed as a circuit are not allowed inside private expressions',
             ast)
     self.visitChildren(ast)
Esempio n. 3
0
    def visit_child_expressions(parent: AST, exprs: List[AST]):
        if len(exprs) > 1:
            modset = set(exprs[0].modified_values.keys())
            for arg in exprs[1:]:
                diffset = modset.intersection(arg.modified_values)
                if diffset:
                    setstr = f'{{{", ".join(map(str, diffset))}}}'
                    raise TypeException(
                        f'Undefined behavior due to potential side effect on the same value(s) \'{setstr}\' in multiple expression children.\n'
                        'Solidity does not guarantee an evaluation order for non-shortcircuit expressions.\n'
                        'Since zkay requires local simulation for transaction transformation, all semantics must be well-defined.',
                        parent)
                else:
                    modset.update(diffset)

            for arg in exprs:
                modset = set(arg.modified_values.keys())
                other_args = [e for e in exprs if e != arg]
                for arg2 in other_args:
                    diffset = modset.intersection(arg2.read_values)
                    if diffset:
                        setstr = f'{{{", ".join([str(val) + (f".{str(member)}" if member else "") for val, member in diffset])}}}'
                        raise TypeException(
                            f'Undefined behavior due to read of value(s) \'{setstr}\' which might be modified in this subexpression.\n'
                            'Solidity does not guarantee an evaluation order for non-shortcircuit expressions.\n'
                            'Since zkay requires local simulation for transaction transformation, all semantics must be well-defined.',
                            arg)
Esempio n. 4
0
    def visitFunctionCallExpr(self, ast: FunctionCallExpr):
        if isinstance(ast.func, BuiltinFunction):
            self.handle_builtin_function_call(ast, ast.func)
        elif ast.is_cast:
            if not isinstance(ast.func.target, EnumDefinition):
                raise NotImplementedError(
                    'User type casts only implemented for enums')
            ast.annotated_type = self.handle_cast(
                ast.args[0], ast.func.target.annotated_type.type_name)
        elif isinstance(ast.func, LocationExpr):
            ft = ast.func.annotated_type.type_name

            if len(ft.parameters) != len(ast.args):
                raise TypeException("Wrong number of arguments", ast.func)

            # Check arguments
            for i in range(len(ast.args)):
                ast.args[i] = self.get_rhs(ast.args[i],
                                           ft.parameters[i].annotated_type)

            # Set expression type to return type
            if len(ft.return_parameters) == 1:
                ast.annotated_type = ft.return_parameters[
                    0].annotated_type.clone()
            else:
                # TODO maybe not None label in the future
                ast.annotated_type = AnnotatedTypeName(
                    TupleType([t.annotated_type
                               for t in ft.return_parameters]), None)
        else:
            raise TypeException('Invalid function call', ast)
Esempio n. 5
0
 def visitDoWhileStatement(self, ast: DoWhileStatement):
     if contains_private_expr(ast.condition):
         raise TypeException(
             'Loop condition cannot contain private expressions',
             ast.condition)
     if contains_private_expr(ast.body):
         raise TypeException('Loop body cannot contain private expressions',
                             ast.body)
     self.visitChildren(ast)
Esempio n. 6
0
 def visitForStatement(self, ast: ForStatement):
     if contains_private_expr(ast.condition):
         raise TypeException(
             'Loop condition cannot contain private expressions',
             ast.condition)
     if contains_private_expr(ast.body):
         raise TypeException('Loop body cannot contain private expressions',
                             ast.body)
     if ast.update is not None and contains_private_expr(ast.update):
         raise TypeException(
             'Loop update statement cannot contain private expressions',
             ast.update)
     self.visitChildren(ast)
Esempio n. 7
0
    def visitConstructorOrFunctionDefinition(
            self, ast: ConstructorOrFunctionDefinition):
        for t in ast.parameter_types:
            if not isinstance(t.privacy_annotation, (MeExpr, AllExpr)):
                raise TypeException(
                    'Only me/all accepted as privacy type of function parameters',
                    ast)

        if ast.can_be_external:
            for t in ast.return_type:
                if not isinstance(t.privacy_annotation, (MeExpr, AllExpr)):
                    raise TypeException(
                        'Only me/all accepted as privacy type of return values for public functions',
                        ast)
Esempio n. 8
0
    def visitReclassifyExpr(self, ast: ReclassifyExpr):
        if not ast.privacy.privacy_annotation_label():
            raise TypeException(
                'Second argument of "reveal" cannot be used as a privacy type',
                ast)

        # NB prevent any redundant reveal (not just for public)
        ast.annotated_type = AnnotatedTypeName(
            ast.expr.annotated_type.type_name, ast.privacy)
        if ast.instanceof(ast.expr.annotated_type) is True:
            raise TypeException(
                f'Redundant "reveal": Expression is already "@{ast.privacy.code()}"',
                ast)
        self.check_for_invalid_private_type(ast)
Esempio n. 9
0
    def visitIdentifierExpr(self, ast: IdentifierExpr):
        if isinstance(ast.target, Mapping):
            # no action necessary, the identifier will be replaced later
            pass
        else:
            target = ast.target
            if isinstance(target, ContractDefinition):
                raise TypeException(
                    f'Unsupported use of contract type in expression', ast)
            ast.annotated_type = target.annotated_type.clone()

            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)
Esempio n. 10
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)
Esempio n. 11
0
    def visitStateVariableDeclaration(self, ast: StateVariableDeclaration):
        if ast.expr:
            # prevent private operations in declaration
            if contains_private(ast):
                raise TypeException(
                    'Private assignments to state variables must be in the constructor',
                    ast)

            # check type
            self.get_rhs(ast.expr, ast.annotated_type)

        # prevent "me" annotation
        p = ast.annotated_type.privacy_annotation
        if p.is_me_expr():
            raise TypeException(f'State variables cannot be annotated as me',
                                ast)
Esempio n. 12
0
 def visitMemberAccessExpr(self, ast: MemberAccessExpr):
     assert ast.target is not None
     if ast.expr.annotated_type.is_address(
     ) and ast.expr.annotated_type.is_private():
         raise TypeException(
             "Cannot access members of private address variable", ast)
     ast.annotated_type = ast.target.annotated_type.clone()
Esempio n. 13
0
 def visitIdentifierExpr(self, ast: IdentifierExpr):
     if ast.is_rvalue() and self.state_vars_assigned is not None:
         if ast.target in self.state_vars_assigned and not self.state_vars_assigned[
                 ast.target]:
             raise TypeException(
                 f'{str(ast)} is reading "final" state variable before writing it',
                 ast)
Esempio n. 14
0
 def visitFunctionCallExpr(self, ast: FunctionCallExpr):
     if not ast.is_cast and isinstance(ast.func, LocationExpr):
         if ast.func.target.requires_verification and ast.func.target.is_recursive:
             raise TypeException(
                 "Non-inlineable call to recursive private function",
                 ast.func)
     self.visitChildren(ast)
Esempio n. 15
0
 def visitFunctionCallExpr(self, ast: FunctionCallExpr):
     if self.evaluate_privately and isinstance(
             ast.func, LocationExpr
     ) and not ast.is_cast and ast.func.target.has_side_effects:
         raise TypeException(
             'Expressions with side effects are not allowed inside private expressions',
             ast)
     self.visitExpression(ast)
Esempio n. 16
0
 def visitAssignmentStatement(self, ast: AssignmentStatement):
     self.visit(ast.rhs)
     if isinstance(ast.lhs, IdentifierExpr):
         var = ast.lhs.target
         if var in self.state_vars_assigned:
             if self.state_vars_assigned[var]:
                 raise TypeException("Tried to reassign final variable",
                                     ast)
             self.state_vars_assigned[var] = True
Esempio n. 17
0
    def visitAssignmentStatement(self, ast: AssignmentStatement):
        if not isinstance(ast.lhs, (TupleExpr, LocationExpr)):
            raise TypeException("Assignment target is not a location", ast.lhs)

        expected_type = ast.lhs.annotated_type
        ast.rhs = self.get_rhs(ast.rhs, expected_type)

        # prevent modifying final
        f = ast.function
        if isinstance(ast.lhs, (IdentifierExpr, TupleExpr)):
            self.check_final(f, ast.lhs)
Esempio n. 18
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)
Esempio n. 19
0
 def collect_modified_values(self, target: Union[Expression, Statement],
                             expr: Union[TupleExpr, LocationExpr]):
     if isinstance(expr, TupleExpr):
         for elem in expr.elements:
             self.collect_modified_values(target, elem)
     else:
         mod_value = InstanceTarget(expr)
         if mod_value in target.modified_values:
             raise TypeException(
                 f'Undefined behavior due multiple different assignments to the same target in tuple assignment',
                 expr)
         target.modified_values[mod_value] = None
Esempio n. 20
0
    def visitReclassifyExpr(self, ast: ReclassifyExpr):
        if not ast.privacy.privacy_annotation_label():
            raise TypeException(
                'Second argument of "reveal" cannot be used as a privacy type',
                ast)

        homomorphism = ast.homomorphism or ast.expr.annotated_type.homomorphism
        assert (homomorphism is not None)

        # Prevent ReclassifyExpr to all with homomorphic type
        if ast.privacy.is_all_expr(
        ) and homomorphism != Homomorphism.NON_HOMOMORPHIC:
            # If the target privacy is all, we infer a target homomorphism of NON_HOMOMORPHIC
            ast.homomorphism = homomorphism = Homomorphism.NON_HOMOMORPHIC

        # Make sure the first argument to reveal / rehom is public or private provably equal to @me
        is_expr_at_all = ast.expr.annotated_type.is_public()
        is_expr_at_me = ast.expr.annotated_type.is_private_at_me(ast.analysis)
        if not is_expr_at_all and not is_expr_at_me:
            raise TypeException(
                f'First argument of "{ast.func_name()}" must be accessible,'
                f'i.e. @all or provably equal to @me', ast)

        # Prevent unhom(public_value)
        if is_expr_at_all and isinstance(
                ast, RehomExpr
        ) and ast.homomorphism == Homomorphism.NON_HOMOMORPHIC:
            raise TypeException(
                f'Cannot use "{ast.homomorphism.rehom_expr_name}" on a public value',
                ast)

        # NB prevent any redundant reveal (not just for public)
        ast.annotated_type = AnnotatedTypeName(
            ast.expr.annotated_type.type_name, ast.privacy, homomorphism)
        if ast.instanceof(ast.expr.annotated_type) is True:
            raise TypeException(
                f'Redundant "{ast.func_name()}": Expression is already '
                f'"@{ast.privacy.code()}{homomorphism}"', ast)
        self.check_for_invalid_private_type(ast)
Esempio n. 21
0
 def visitIfStatement(self, ast: IfStatement):
     old_in_privif_stmt = self.inside_privif_stmt
     if ast.condition.annotated_type.is_private():
         mod_vals = set(ast.then_branch.modified_values.keys())
         if ast.else_branch is not None:
             mod_vals = mod_vals.union(ast.else_branch.modified_values)
         for val in mod_vals:
             if not val.target.annotated_type.zkay_type.type_name.is_primitive_type(
             ):
                 raise TypeException(
                     'Writes to non-primitive type variables are not allowed inside private if statements',
                     ast)
             if val.in_scope_at(
                     ast) and not ast.before_analysis.same_partition(
                         val.privacy, Expression.me_expr()):
                 raise TypeException(
                     'If statement with private condition must not contain side effects to variables with owner != me',
                     ast)
         self.inside_privif_stmt = True
         self.priv_setter.set_evaluation(ast, evaluate_privately=True)
     self.visitChildren(ast)
     self.inside_privif_stmt = old_in_privif_stmt
Esempio n. 22
0
    def visitIfStatement(self, ast: IfStatement):
        self.visit(ast.condition)
        prev = self.state_vars_assigned.copy()
        self.visit(ast.then_branch)
        then_b = self.state_vars_assigned.copy()
        self.state_vars_assigned = prev
        if ast.else_branch is not None:
            self.visit(ast.else_branch)

        assert then_b.keys() == self.state_vars_assigned.keys()
        for var in then_b.keys():
            if then_b[var] != self.state_vars_assigned[var]:
                raise TypeException(
                    "Final value is not assigned in both branches", ast)
Esempio n. 23
0
    def visitContractDefinition(self, ast: ContractDefinition):
        self.state_vars_assigned = {}
        for v in ast.state_variable_declarations:
            if v.is_final and v.expr is None:
                self.state_vars_assigned[v] = False

        if len(ast.constructor_definitions) > 0:
            assert (len(ast.constructor_definitions) == 1)
            c = ast.constructor_definitions[0]
            self.visit(c.body)

        for sv, assigned in self.state_vars_assigned.items():
            if not assigned:
                raise TypeException("Did not set all final state variables",
                                    sv)

        self.state_vars_assigned = None
Esempio n. 24
0
    def visitAssignmentStatement(self, ast: AssignmentStatement):
        # NB TODO? Should we optionally disallow writes to variables which are owned by someone else (with e.g. a new modifier)
        #if ast.lhs.annotated_type.is_private():
        #    expected_rhs_type = AnnotatedTypeName(ast.lhs.annotated_type.type_name, Expression.me_expr())
        #    if not ast.lhs.instanceof(expected_rhs_type):
        #        raise TypeException("Only owner can assign to its private variables", ast)

        if not isinstance(ast.lhs, (TupleExpr, LocationExpr)):
            raise TypeException("Assignment target is not a location", ast.lhs)

        expected_type = ast.lhs.annotated_type
        ast.rhs = self.get_rhs(ast.rhs, expected_type)

        # prevent modifying final
        f = ast.function
        if isinstance(ast.lhs, (IdentifierExpr, TupleExpr)):
            self.check_final(f, ast.lhs)
Esempio n. 25
0
 def check_final(self, fct: ConstructorOrFunctionDefinition,
                 ast: Expression):
     if isinstance(ast, IdentifierExpr):
         target = ast.target
         if hasattr(target, 'keywords'):
             if 'final' in target.keywords:
                 if isinstance(
                         target,
                         StateVariableDeclaration) and fct.is_constructor:
                     # assignment allowed
                     pass
                 else:
                     raise TypeException('Modifying "final" variable', ast)
     else:
         assert isinstance(ast, TupleExpr)
         for elem in ast.elements:
             self.check_final(fct, elem)
Esempio n. 26
0
    def visitReclassifyExpr(self, ast: ReclassifyExpr):
        if self.inside_privif_stmt and not ast.statement.before_analysis.same_partition(
                ast.privacy.privacy_annotation_label(), Expression.me_expr()):
            raise TypeException(
                'Revealing information to other parties is not allowed inside private if statements',
                ast)

        if ast.expr.annotated_type.is_public():
            eval_in_public = False
            try:
                self.priv_setter.set_evaluation(ast, evaluate_privately=True)
            except TypeException:
                eval_in_public = True
            if eval_in_public or not self.should_evaluate_public_expr_in_circuit(
                    ast.expr):
                self.priv_setter.set_evaluation(ast.expr,
                                                evaluate_privately=False)
        else:
            self.priv_setter.set_evaluation(ast, evaluate_privately=True)
        self.visit(ast.expr)
Esempio n. 27
0
    def visitAnnotatedTypeName(self, ast: AnnotatedTypeName):
        if type(ast.type_name) == UserDefinedTypeName:
            if not isinstance(ast.type_name.target, EnumDefinition):
                raise TypeException('Unsupported use of user-defined type',
                                    ast.type_name)
            ast.type_name = ast.type_name.target.annotated_type.type_name.clone(
            )

        if ast.privacy_annotation != Expression.all_expr():
            if not ast.type_name.can_be_private():
                raise TypeException(
                    f'Currently, we do not support private {str(ast.type_name)}',
                    ast)
            if ast.homomorphism != Homomorphism.NON_HOMOMORPHIC:
                # only support uint8, uint16, uint24, uint32 homomorphic data types
                if not ast.type_name.is_numeric:
                    raise TypeException(
                        f'Homomorphic type not supported for {str(ast.type_name)}: Only numeric types supported',
                        ast)
                elif ast.type_name.signed:
                    raise TypeException(
                        f'Homomorphic type not supported for {str(ast.type_name)}: Only unsigned types supported',
                        ast)
                elif ast.type_name.elem_bitwidth > 32:
                    raise TypeException(
                        f'Homomorphic type not supported for {str(ast.type_name)}: Only up to 32-bit numeric types supported',
                        ast)

        p = ast.privacy_annotation
        if isinstance(p, IdentifierExpr):
            t = p.target
            if isinstance(t, Mapping):
                # no action necessary, this is the case: mapping(address!x => uint@x)
                pass
            elif not t.is_final and not t.is_constant:
                raise TypeException(
                    'Privacy annotations must be "final" or "constant", if they are expressions',
                    p)
            elif t.annotated_type != AnnotatedTypeName.address_all():
                raise TypeException(
                    f'Privacy type is not a public address, but {str(t.annotated_type)}',
                    p)
Esempio n. 28
0
 def check_for_invalid_private_type(ast):
     assert hasattr(ast, 'annotated_type')
     at = ast.annotated_type
     if at.is_private() and not at.type_name.can_be_private():
         raise TypeException(f"Type {at.type_name} cannot be private",
                             ast.annotated_type)
Esempio n. 29
0
 def visitMapping(self, ast: Mapping):
     if ast.key_label is not None:
         if ast.key_type != TypeName.address_type():
             raise TypeException(f'Only addresses can be annotated', ast)
Esempio n. 30
0
 def visitRequireStatement(self, ast: RequireStatement):
     if not ast.condition.annotated_type.privacy_annotation.is_all_expr():
         raise TypeException(f'require needs public argument', ast)