Random

Shift Operations in TFHE

The TFHE.shr and TFHE.shl shift operations work with an encrypted integer type (euintX) as the first operand and either uint8 or euint8 as the second operand. The second operand is computed modulo the bit width of the first operand.

Example:

TFHE.shr(euint64 x, 70);

This results in:

TFHE.shr(euint64 x, 6); // Because 70 % 64 = 6

This differs from standard Solidity shifts where, for instance, a right shift (>>) could result in a null value if the operand exceeds the bit width.

Supported Operators for Encrypted Integers

TFHE supports overloaded operators like +, -, *, &, etc., for encrypted integers. These operators invoke versions without overflow checks by default.

Comparison Operations

In Fully Homomorphic Encryption (FHE), comparison operations yield encrypted boolean results of type ebool. As ebool maintains confidentiality, it cannot be directly used in standard boolean operations.

To address this, the fhEVM framework provides the select function, which is similar to a ternary operator. It enables conditional assignments based on ebool values.

Example:

function bid(einput encryptedValue, bytes calldata inputProof) public onlyBeforeEnd {
    euint64 bid = TFHE.asEuint64(encryptedValue, inputProof);
    ebool isAbove = TFHE.lt(highestBid, bid);
    highestBid = TFHE.select(isAbove, bid, highestBid);
    TFHE.allow(highestBid, address(this));
}

Explanation:

  1. Decryption: The encryptedValue is decrypted into an euint64 type using TFHE.asEuint64, preserving confidentiality.

  2. Comparison: The TFHE.lt function compares the current highestBid with the new bid, producing an ebool that indicates if the new bid is higher.

  3. Conditional Assignment: The TFHE.select function updates highestBid based on the isAbove condition.

Error Handling in Encrypted Smart Contracts

Error handling requires custom strategies, as failed conditions in encrypted contracts do not automatically revert transactions. Instead, an error handler records the latest error information for each wallet.

Example:

struct LastError {
    euint8 error;
    uint timestamp;
}

euint8 internal NO_ERROR;
euint8 internal NOT_ENOUGH_FUND;

constructor() {
    NO_ERROR = TFHE.asEuint8(0);
    NOT_ENOUGH_FUND = TFHE.asEuint8(1);
}

function setLastError(euint8 error, address addr) private {
    _lastErrors[addr] = LastError(error, block.timestamp);
    emit ErrorChanged(addr);
}

function _transfer(address from, address to, euint32 amount) internal {
    ebool canTransfer = TFHE.le(amount, balances[from]);
    euint64 transferValue = TFHE.select(canTransfer, amount, TFHE.asEuint64(0));
    setLastError(TFHE.select(canTransfer, NO_ERROR, NOT_ENOUGH_FUND), msg.sender);
    balances[to] = TFHE.add(balances[to], transferValue);
    TFHE.allow(balances[to], address(this));
    TFHE.allow(balances[to], to);
    balances[from] = TFHE.sub(balances[from], transferValue);
    TFHE.allow(balances[from], address(this));
    TFHE.allow(balances[from], from);
}

Random Number Generation

TFHE enables fully on-chain random number generation with the following functions:

euint8 r8 = TFHE.randEuint8();
euint16 r16 = TFHE.randEuint16();
euint32 r32 = TFHE.randEuint32();
euint64 r64 = TFHE.randEuint64();

Encrypted Inputs

Encrypted inputs are fundamental to fhEVM, allowing users to push encrypted data onto the blockchain securely. Users must provide proof of knowledge of the plaintext to prevent ciphertext reuse.

Function Example:

function myExample(
    address account,
    einput param1,
    uint id,
    einput param2,
    einput param3,
    bool isAllowed,
    bytes calldata inputProof
) public {
    // Implementation
}

Client-Side Implementation:

const instance = await createInstance({ networkUrl: "http://localhost:8545" });

// Create encrypted inputs
const input = instance.createEncryptedInput(contractAddress, userAddress);
const inputs = input.add64(64).addBool(true).add8(4).encrypt(); // Encrypt the three parameters

// Call the smart contract function with encrypted parameters
contract.myExample(
    "0xa5e1defb98EFe38EBb2D958CEe052410247F4c80", // account address
    inputs.handles[0],                          // param1
    32,                                         // id
    inputs.handles[1],                          // param2
    inputs.handles[2],                          // param3
    true,                                       // isAllowed
    inputs.inputProof                           // inputProof
);

Access Control List (ACL) System

fhEVM includes an ACL system to define which addresses can manipulate ciphertexts. This feature ensures that unauthorized addresses cannot access or modify the contents of any ciphertext. Two types of allowances are supported:

Permanent Allowance:

TFHE.allow(ciphertext, address);
  • Grants permanent access to a ciphertext for a specific address on the blockchain.

  • The ciphertext can be used by the authorized address at any time.

  • The ACL is stored in a dedicated contract.

Temporary Allowance:

TFHE.allowTransient(ciphertext, address);
  • Grants temporary access to a ciphertext for the duration of a transaction.

  • The ACL is stored in transient storage, saving gas costs.

  • Particularly useful when calling an external function using a ciphertext as a parameter.

Example: Function Calling Another Function

contract SecretGiver {
    SecretStore public secretStore;

    constructor() {
        secretStore = new SecretStore();
    }

    function giveMySecret() public {
        euint16 mySecret = TFHE.asEuint16(42);
        TFHE.allowTransient(mySecret, address(secretStore));
        secretStore.storeSecret(mySecret);
    }
}

Last updated