Control Structures

There are a 3 ways in which the encrypted boolean (ebool), generated from TFHE comparisons, could be used in the design logic.

  • TFHE.cmux

  • require(TFHE.decrypt(someEbool))

  • TFHE.optReq

  • TFHE.isInitialized

TFHE.cmux

The TFHE.cmux multiplexer function allows you to pass in the ebool as the first argument and returns the second argument if the ebool is true, and third argument if it's false. This is particularly useful to avoid information leakage with the if/else statement for encrypted conditions from an outside perspective.

// hiddenValue will be 1 if eboolValue is true, and 0 if false
euint8 hiddenValue = TFHE.cmux(eboolValue, TFHE.asEuint8(1), TFHE.asEuint8(0));

In the Private Voting example, we first compare whether or not the encryptedChoice matches 1 or 0, which gives us an ebool. If the encrypted boolean is a true value, then inFavorCountToCast will take on the value of encryptedVoteCount. If it's a false value, it will take on the value of 0 (but it's also encrypted). Therefore from an outside perspective, you wouldn't know if inFavorCountToCast was assigned the actual vote count or 0. The same logic is applied to againstCountToCast but in reverse. And finally, both inFavorCountToCast and againstCountToCast are added to the encrypted tallies, inFavorCountEncrypted, and againstCountEncrypted, but only one of them would have incremented.

    // encryptedChoice can be 0 (against) or 1 (in favor)
    function castVote(bytes calldata encryptedVoteCount, bytes calldata encryptedChoice) public {
        ebool choice = TFHE.eq(TFHE.asEuint8(1), TFHE.asEuint8(encryptedChoice));
        euint8 inFavorCountToCast = TFHE.cmux(choice, TFHE.asEuint8(encryptedVoteCount), TFHE.asEuint8(0));
        euint8 againstCountToCast = TFHE.cmux(choice, TFHE.asEuint8(0), TFHE.asEuint8(encryptedVoteCount));
        inFavorCountEncrypted = TFHE.add(inFavorCountEncrypted, inFavorCountToCast);
        againstCountEncrypted = TFHE.add(againstCountEncrypted, againstCountToCast);
    }

require(TFHE.decrypt(someEbool))

To fail a transaction in case a condition is not met, you could decrypt the ebool after a comparison, and use the require from solidity.

// This function will revert.
function isEqualTo10() public {
    euint8 val = TFHE.asEuint8(4);
    ebool isEqual = TFHE.eq(val, TFHE.asEuint8(10));
    require(TFHE.decrypt(isEqual), "Not equal to 10");
}

TFHE.optReq

TFHE.optReq is an optimistic encrypted require statements that would fail a transaction if an ebool condition is false. This operation is executed at the end of the execution, offering 2 benefits: 1) By executing this operation at the end of the transaction, we mitigate the risk of information leakage. Alternatively, if it were executed at various points in the code, an observer could potentially discern its execution based on gas usage. Placing it at the end effectively prevents such inference 2) Batching all the TFHE.optReq at the end of the execution helps on the performance, given that this process is performed by threshold decryption.

// This function will revert.
function someLogic() public {
    euint8 val = TFHE.asEuint8(4);
    ebool isEqual = TFHE.eq(val, TFHE.asEuint8(10));
    TFHE.optReq(isEqual); // this is only executed at the end, preventing information leakage
    // some other logic
}

TFHE.isInitialized

TFHE.isInitialized returns a boolean that specifies whether or not an euint has been initialized.

euint32 existingBid = bids[msg.sender];
if (TFHE.isInitialized(existingBid)) {
    // Some logic
 } else {
    // Some logic
 }

Last updated