Decrypt/SealOutput from Cofhejs
Overview
This document lays out the complete flow of an off-chain sealoutput request. It is recommended to always use cofhejs.unseal rather than cofhejs.decrypt as .unseal internally seals the user's data before returning it from the Threshold Network, making it inherently more secure (eg. man in the middle attack).
Note: The example below is of
cofhejs.unseal, howevercofhejs.decryptuses the same API and returns the same result.
Key Components
| Component | Description |
|---|---|
| CtHash | The encrypted hash representing an encrypted number. Fetched on-chain. |
| Cofhejs | Javascript library handling permits and the unseal / decrypt operations. |
| Threshold Network | Decentralized decryption network that handles the requests |
| ACL | On-chain Access Control List responsible for tracking CtHash access. |
Flow Diagram
The following diagram illustrates the complete flow of an Decrypt/SealOutput request in the CoFHE ecosystem:
Step-by-Step Flow
📌 Step 1: Fetching of CtHash
Solidity contract:
contract
function setNumber(uint32 num) public {
counter[msg.sender] = FHE.asEuint32(num);
FHE.allowSender(counter[msg.sender]);
}
function getNumber() public view returns (euint64) {
return counter[msg.sender];
}
- Fetch the user's
euint64from the chain by callingconst CtHash = await example.getNumber()which returns aneuint64as a js bigint.
Note: All euints, along with ebool and eaddress, are wrappers around uint256. The data returned from
example.getNumber()is in the type bigint, and can be treated as aCtHashdirectly
📌 Step 2: Integration with Cofhejs
-
The decentralized application (dApp) integrates with CoFHE by utilizing Cofhejs for encryption. See in GitHub
-
Create a permit using
cofhejs.createPermit(...). This permit will automatically be used in the following step. -
Unseal using
cofhejs.unseal(CtHash). Calls
/sealoutputon the threshold network, unseals the result.
📌 Step 3 (Handled by cofhejs.unseal):
-
cofhejs.unsealcalls /sealoutput. The user's Permit is added to the request.Permit.issuershould be themsg.senderin Step 1 for the permit to be valid. https://{ThresholdNetworkUrl}/sealoutput -
Threshold Network makes an on-chain call to the
ACLto verify that the Permit is valid. -
ACL verifies that the Permit is valid.
-
ACL verifies that
Permit.issuerhas been granted access toCtHash. (Access is granted byFHE.allowSenderin the Example contract functionsetNumber()) -
Threshold Network seals the data with
Permit.sealingKey -
Threshold Network returns the sealed result to
cofhejs
📌 Step 4: Handling Results
cofhejs receives the result from the Threshold Network and:
-
Unseals the result using the private_key of the sealing key pair. The result is always unsealed as a bigint regardless of the type of CtHash (euint32 / ebool / eaddress)
-
Cofhejs converts the output type as follows:
export const convertViaUtype = <U extends FheTypes>(
utype: U,
value: bigint
): UnsealedItem<U> => {
if (utype === FheTypes.Bool) {
return !!value as UnsealedItem<U>;
} else if (utype === FheTypes.Uint160) {
return uint160ToAddress(value) as UnsealedItem<U>;
} else if (utype == null || FheUintUTypes.includes(utype as number)) {
return value as UnsealedItem<U>;
} else {
throw new Error(`convertViaUtype :: invalid utype :: ${utype}`);
}
};
- The result is returned as a
Resulttype. TheResult<T>type looks like this:
export type Result<T, E = string> =
| { success: true; data: T; error: null }
| { success: false; data: null; error: E };
The Result type is a discriminated union that represents either:
- A successful operation with data (
success: true) - A failed operation with an error message (
success: false). The return type ofcofhejs.unsealis determined by the utype passed in as the second argument:
const boolResult: Result<bool> = await cofhejs.unseal(
boolCtHash,
FheTypes.Bool
);
const uintResult: Result<bigint> = await cofhejs.unseal(
uintCtHash,
FheTypes.Uint32
);
const addressResult: Result<string> = await cofhejs.unseal(
ctHash,
FheTypes.Address
);