Global Decryption

Global Decryption

The global decryption operation is asynchronous, and to enable decryption, the application contract must extend the GatewayCaller contract, which will automatically import the GatewayCaller solidity library.

pragma solidity ^0.8.24;

import "fhevm/lib/TFHE.sol";
import "fhevm/gateway/GatewayCaller.sol";

contract TestDecryptingRandomNumber is GatewayCaller{
    uint8 public randomNumber;
    
function decryptRandomNumber() public {
    euint8 randomNumber = TFHE.randEuint8();
    TFHE.allow(randomNumber, address(this));
    uint256[] memory cts = new uint256[](1);
    cts[0] = Gateway.toUint256(randomNumber);
    uint256 requestId = Gateway.requestDecryption(cts, this.myCustomCallback.selector, 0, block.timestamp + 100, false);
}

function myCustomCallback(uint256 /*requestId*/, uint8 decryptedInput) public onlyGateway returns (bool) {
    randomNumber = decryptedInput;
    return true;
}

Explanation of Steps: After generating a random number using randEuint8(), the contract must be permitted to access the unique ciphertext handle by calling TFHE.allow(). Next, prepare the list of ciphertext handles to be decrypted—in this example, a single random number. Calling requestDecryption will emit an EventDecryption event on the GatewayContract, which will then be picked up by the relayer.

More about the requestDecryption method:

function requestDecryption(
    uint256[] memory ct,
    bytes4 callbackSelector,
    uint256 msgValue,
    uint256 maxTimestamp,
    bool passSignaturesToCaller
) returns(uint256 requestID)

Explanation of arguments:

  • ct: An array of unique ciphertext handles is requested for decryption. These are uint256 values derived from unwrapping the unique ciphertext handles provided, such as euint4, euint8, euint16, euint32, euint64, ebool, eaddress, and ebytes256.

  • CallbackSelector: The function selector for the callback function, which the Gateway contract will call once the relayer fulfills the decryption request.

  • msgValue : The amount of native tokens to be sent to the calling contract upon fulfillment, i.e., when the callback is returned with the decryption results.

  • maxTimestamp : The time after which the callback will not be accepted.

  • requestId : The requestId is returned by requestDecryption and can be useful for providing data in the callback function.

  • passSignaturesToCaller : If set to false, the callback function does not require signatures to accompany the decrypted value. If set to true, the callback function will include signatures from the KMS, removing the dependency on trusting the Gateway.

function myCustomCallback(uint256 /*requestID*/, uint8 decryptedInput, bytes[] memory signatures) external onlyGateway returns (bool)

How to pass data to callbacks:

There are two ways to pass data to the callback function. Depending on the amount of data, you can decide which method suits you best.

  1. Predefined functions: If you need to pass a few arguments, you can use our predefined functions. A small snippet demonstrating how to use them is shown below. You can find a list of all predefined functions we support, including both addParams() and getParams() at the following link.

mapping (address => uint8) public userAddressToRandomNumber; 

function decryptRandomNumber(address userAddress) public {
    euint8 randomNumber = TFHE.randEuint8();
    TFHE.allow(randomNumber, address(this));
    uint256[] memory cts = new uint256[](1);
    cts[0] = Gateway.toUint256(randomNumber);
    uint256 requestId = Gateway.requestDecryption(cts, this.myCustomCallback.selector, 0, block.timestamp + 100, false);
    
    // Adding the user address to the request parameters
    addParamsAddress(requestId, userAddress);
}

function myCustomCallback(uint256 requestId, uint8 decryptedInput) public onlyGateway returns (bool) {
    // Retrieving parameters using the request ID
    address[] memory params = getParamsAddress(requestId);
    userAddressToRandomNumber[params[0]] = decryptedInput;
    return true;
}
  1. Custom Data Holder for Callback Functions: Suppose you have structured data that you want to retain as-is. You can use a combination of mappings and structs to access this data in the callback, as shown below.

struct CallbackData {
    address userAddress;
    uint8 someNumber;
    uint256 timestamp;
    string message;
    }
    
mapping(uint256 => CallbackData) public requestIdToData;

function decryptRandomNumber(uint8 someNumber, string memory message) public {
    euint8 randomNumber = TFHE.randEuint8();
    TFHE.allow(randomNumber, address(this));
    uint256[] memory cts = new uint256[](1);
    cts[0] = Gateway.toUint256(randomNumber);
    uint256 requestId = Gateway.requestDecryption(cts, this.myCustomCallback.selector, 0, block.timestamp + 100, false);
    
    // Adding the user address to the request parameters
    requestIdToData[requestId] = CallbackData({
            userAddress: msg.sender,
            someNumber: someNumber,
            timestamp: block.timestamp,
            message: message
        });
}

function myCustomCallback(uint256 requestId, uint8 decryptedInput) public onlyGateway returns (bool) {
    // Retrieving parameters using the request ID
    CallbackData memory data = requestIdToData[requestId];
    // access data and do computation here 
    delete requestIdToData[requestId];
    return true;
}

Last updated