def evaluate_expr_in_circuit(self, expr: Expression, new_privacy: PrivacyLabelExpr, homomorphism: Homomorphism) -> LocationExpr: """ Evaluate private expression and return result as a fresh out variable. Roughly corresponds to out() from paper Note: This function has side effects on expr.statement (adds a pre_statement) :param expr: [SIDE EFFECT] The expression which should be evaluated privately :param new_privacy: The circuit output should be encrypted for this owner (or plain if 'all') :return: Location expression which references the encrypted circuit result """ with self.circ_indent_block(expr.code()): return self._get_circuit_output_for_private_expression( expr, new_privacy, homomorphism)
def add_to_circuit_inputs(self, expr: Expression) -> HybridArgumentIdf: """ Add the provided expression to the public circuit inputs. Roughly corresponds to in() from paper If expr is encrypted (privacy != @all), this function also automatically ensures that the circuit has access to the correctly decrypted expression value in the form of a new private circuit input. If expr is an IdentifierExpr, its value will be cached (i.e. when the same identifier is needed again as a circuit input, its value will be retrieved from cache rather \ than adding an expensive redundant input. The cache is invalidated as soon as the identifier is overwritten in public code) Note: This function has side effects on expr.statement (adds a pre_statement) :param expr: [SIDE EFFECT] expression which should be made available inside the circuit as an argument :return: HybridArgumentIdf which references the plaintext value of the newly added input """ privacy = expr.annotated_type.privacy_annotation.privacy_annotation_label( ) if expr.annotated_type.is_private() else Expression.all_expr() is_public = privacy == Expression.all_expr() expr_text = expr.code() input_expr = self._expr_trafo.visit(expr) t = input_expr.annotated_type.type_name locally_decrypted_idf = None # If expression has literal type -> evaluate it inside the circuit (constant folding will be used) # rather than introducing an unnecessary public circuit input (expensive) if isinstance(t, BooleanLiteralType): return self._evaluate_private_expression(input_expr, str(t.value)) elif isinstance(t, NumberLiteralType): return self._evaluate_private_expression(input_expr, str(t.value)) t_suffix = '' if isinstance(expr, IdentifierExpr): # Look in cache before doing expensive move-in if self._remapper.is_remapped(expr.target.idf): remapped_idf = self._remapper.get_current(expr.target.idf) return remapped_idf t_suffix = f'_{expr.idf.name}' # Generate circuit inputs if is_public: tname = f'{self._in_name_factory.get_new_name(expr.annotated_type.type_name)}{t_suffix}' return_idf = input_idf = self._in_name_factory.add_idf( tname, expr.annotated_type.type_name) self._phi.append(CircComment(f'{input_idf.name} = {expr_text}')) else: # Encrypted inputs need to be decrypted inside the circuit (i.e. add plain as private input and prove encryption) tname = f'{self._secret_input_name_factory.get_new_name(expr.annotated_type.type_name)}{t_suffix}' return_idf = locally_decrypted_idf = self._secret_input_name_factory.add_idf( tname, expr.annotated_type.type_name) cipher_t = TypeName.cipher_type(input_expr.annotated_type, expr.annotated_type.homomorphism) tname = f'{self._in_name_factory.get_new_name(cipher_t)}{t_suffix}' input_idf = self._in_name_factory.add_idf( tname, cipher_t, IdentifierExpr(locally_decrypted_idf)) # Add a CircuitInputStatement to the solidity code, which looks like a normal assignment statement, # but also signals the offchain simulator to perform decryption if necessary expr.statement.pre_statements.append( CircuitInputStatement(input_idf.get_loc_expr(), input_expr)) if not is_public: # Check if the secret plain input corresponds to the decrypted cipher value crypto_params = cfg.get_crypto_params( expr.annotated_type.homomorphism) self._phi.append( CircComment( f'{locally_decrypted_idf} = dec({expr_text}) [{input_idf.name}]' )) self._ensure_encryption(expr.statement, locally_decrypted_idf, Expression.me_expr(), crypto_params, input_idf, False, True) # Cache circuit input for later reuse if possible if cfg.opt_cache_circuit_inputs and isinstance(expr, IdentifierExpr): # TODO: What if a homomorphic variable gets used as both a plain variable and as a ciphertext? # This works for now because we never perform homomorphic operations on variables we can decrypt. self._remapper.remap(expr.target.idf, return_idf) return return_idf