This section explains how to handle decryption in fhEVM. Decryption allows plaintext data to be accessed when required for contract logic or user presentation, ensuring confidentiality is maintained throughout the process.
Decryption in fhEVM is an asynchronous process that involves the Gateway and Key Management System (KMS). Contracts requiring decryption must extend the GatewayCaller contract, which imports the necessary libraries and provides access to the Gateway.
Here’s an example of how to request decryption in a contract:
GatewayCaller import:
The GatewayCaller contract is imported to enable decryption requests.
import"fhevm/gateway/GatewayCaller.sol";
Applying decryption to the counter example
Remember our Encrypted Counter contract from before? Here’s an improved version of it, upgraded to support decryption:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "fhevm/lib/TFHE.sol";
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { SepoliaZamaGatewayConfig } from "fhevm/config/ZamaGatewayConfig.sol";
import "fhevm/gateway/GatewayCaller.sol";
/// @title EncryptedCounter3
/// @notice A contract that maintains an encrypted counter and is meant for demonstrating how decryption works
/// @dev Uses TFHE library for fully homomorphic encryption operations and Gateway for decryption
/// @custom:experimental This contract is experimental and uses FHE technology with decryption capabilities
contract EncryptedCounter3 is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, GatewayCaller {
/// @dev Decrypted state variable
euint8 internal counter;
uint8 public decryptedCounter;
constructor() {
Gateway.setGateway(Gateway.defaultGatewayAddress());
// Initialize counter with an encrypted zero value
counter = TFHE.asEuint8(0);
TFHE.allowThis(counter);
}
function incrementBy(einput amount, bytes calldata inputProof) public {
// Convert input to euint8 and add to counter
euint8 incrementAmount = TFHE.asEuint8(amount, inputProof);
counter = TFHE.add(counter, incrementAmount);
TFHE.allowThis(counter);
}
/// @notice Request decryption of the counter value
function requestDecryptCounter() public {
uint256[] memory cts = new uint256[](1);
cts[0] = Gateway.toUint256(counter);
Gateway.requestDecryption(cts, this.callbackCounter.selector, 0, block.timestamp + 100, false);
}
/// @notice Callback function for counter decryption
/// @param decryptedInput The decrypted counter value
/// @return The decrypted value
function callbackCounter(uint256, uint8 decryptedInput) public onlyGateway returns (uint8) {
decryptedCounter = decryptedInput;
return decryptedInput;
}
/// @notice Get the decrypted counter value
/// @return The decrypted counter value
function getDecryptedCounter() public view returns (uint8) {
return decryptedCounter;
}
}
Tests for EncryptedCounter3
Here’s a sample test for the Encrypted Counter contract using Hardhat:
import { awaitAllDecryptionResults, initGateway } from"../asyncDecrypt";import { createInstance } from"../instance";import { getSigners, initSigners } from"../signers";import { expect } from"chai";import { ethers } from"hardhat";describe("EncryptedCounter3",function () {before(asyncfunction () {awaitinitSigners(); // Initialize signersthis.signers =awaitgetSigners();awaitinitGateway(); // Initialize the gateway for decryption });beforeEach(asyncfunction () {constCounterFactory=awaitethers.getContractFactory("EncryptedCounter3");this.counterContract =awaitCounterFactory.connect(this.signers.alice).deploy();awaitthis.counterContract.waitForDeployment();this.contractAddress =awaitthis.counterContract.getAddress();this.instances =awaitcreateInstance(); // Set up instances for testing });it("should increment counter and decrypt the result",asyncfunction () {// Create encrypted input for amount to increment byconstinput=this.instances.createEncryptedInput(this.contractAddress,this.signers.alice.address);input.add8(5); // Increment by 5 as an exampleconstencryptedAmount=awaitinput.encrypt();// Call incrementBy with encrypted amountconsttx=awaitthis.counterContract.incrementBy(encryptedAmount.handles[0],encryptedAmount.inputProof);awaittx.wait();consttx4=awaitthis.counterContract.connect(this.signers.carol).requestDecryptCounter();awaittx4.wait();// Wait for decryption to completeawaitawaitAllDecryptionResults();// Check decrypted value (should be 3: initial 0 + three increments)constdecryptedValue=awaitthis.counterContract.getDecryptedCounter();expect(decryptedValue).to.equal(5); });});
Key additions in testing
Initialize the Gateway:
awaitinitGateway(); // Initialize the gateway for decryption