From 883575ad7a65dbd90c71015493eca0515424eb6d Mon Sep 17 00:00:00 2001 From: Alexandre Carvalheira Date: Mon, 23 Mar 2026 21:23:18 -0300 Subject: [PATCH 1/3] Add ECDSA decrypt result signature docs (from PR #8) Integrates content from PR #8 (docs/ecdsa-decrypt-signatures) into the current doc structure: TaskManager signature verification section, Threshold Network Dispatcher signing, two-path result delivery flow, client-published decryption method, and FHE.sol publishDecryptResultBatch / verifyDecryptResultSafe API reference. Renames decrypt-from-cofhejs to off-chain-decryption-flow. --- deep-dive/cofhe-components/task-manager.mdx | 36 ++++++- .../cofhe-components/threshold-network.mdx | 33 +++++++ .../data-flows/decryption-request-flow.mdx | 19 +++- ...hejs.mdx => off-chain-decryption-flow.mdx} | 0 docs.json | 2 +- .../core-concepts/decryption-operations.mdx | 38 ++++++++ fhe-library/reference/fhe-sol.mdx | 94 ++++++++++++++----- 7 files changed, 189 insertions(+), 33 deletions(-) rename deep-dive/data-flows/{decrypt-from-cofhejs.mdx => off-chain-decryption-flow.mdx} (100%) diff --git a/deep-dive/cofhe-components/task-manager.mdx b/deep-dive/cofhe-components/task-manager.mdx index 700c8f8..19df020 100644 --- a/deep-dive/cofhe-components/task-manager.mdx +++ b/deep-dive/cofhe-components/task-manager.mdx @@ -1,6 +1,6 @@ --- title: TaskManager -description: "On-chain entry point for CoFHE integration that initiates FHE operations and generates unique handles for encrypted computation tasks" +description: "On-chain entry point for CoFHE integration that initiates FHE operations, generates unique handles, and verifies decrypt result signatures" --- @@ -8,6 +8,38 @@ description: "On-chain entry point for CoFHE integration that initiates FHE oper |---------|-------------| | **Type** | Contract deployed on the destination blockchain | | **Function** | Acts as the on-chain entry point for CoFHE integration | -| **Responsibilities** | • Initiates FHE operations by serving as the on-chain entry point. The dApp contract calls the FHE.sol library which triggers the TaskManager contract to submit a new encrypted computation task.
• Generates unique handles that act as references to the results of FHE operations. These results are computed asynchronously off-chain.
• Emits structured events containing the unique handle of the ciphertext, operation type, and other required metadata. | +| **Responsibilities** | • Initiates FHE operations by serving as the on-chain entry point. The dApp contract calls the FHE.sol library which triggers the TaskManager contract to submit a new encrypted computation task.
• Generates unique handles that act as references to the results of FHE operations. These results are computed asynchronously off-chain.
• Emits structured events containing the unique handle of the ciphertext, operation type, and other required metadata.
• Verifies ECDSA signatures on client-published decrypt results and stores them on-chain. | | **Deployment** | A separate Task Manager Contract is deployed for each supported destination chain, enabling chain-specific integrations | +## Decrypt Result Signature Verification + +The TaskManager supports **permissionless publishing of decrypt results**. Anyone holding a valid ECDSA signature from the Threshold Network's Dispatcher can publish a decrypt result on-chain. The TaskManager verifies the signature before storing the result. + +### Key State + +| Variable | Description | +|----------|-------------| +| `decryptResultSigner` | Address of the authorized Threshold Network signer. Set to `address(0)` to skip verification (debug mode). | + +### Functions + +| Function | Description | +|----------|-------------| +| `publishDecryptResult(ctHash, result, signature)` | Verify signature and store the decrypt result on-chain. Emits `DecryptionResult`. | +| `publishDecryptResultBatch(ctHashes[], results[], signatures[])` | Batch publish multiple results in one transaction for gas efficiency. | +| `verifyDecryptResult(ctHash, result, signature)` | Verify a signature without publishing (view). Reverts on failure. | +| `verifyDecryptResultSafe(ctHash, result, signature)` | Verify a signature without publishing (view). Returns `false` on failure. | +| `setDecryptResultSigner(address)` | Admin-only. Set the authorized signer address. | + +### Signature Message Format + +The signed message is a fixed **76-byte** buffer: + +| Field | Size | Encoding | +|-------|------|----------| +| `result` | 32 bytes | uint256, big-endian, left-padded with zeros | +| `enc_type` | 4 bytes | i32, big-endian (extracted from ctHash metadata) | +| `chain_id` | 8 bytes | u64, big-endian (from `block.chainid`) | +| `ct_hash` | 32 bytes | uint256, big-endian | + +The message is hashed with `keccak256` and verified using OpenZeppelin's `ECDSA.tryRecover`. The `enc_type` and `chain_id` are derived on-chain, binding each signature to a specific ciphertext type and chain. diff --git a/deep-dive/cofhe-components/threshold-network.mdx b/deep-dive/cofhe-components/threshold-network.mdx index a51ca5b..6af3220 100644 --- a/deep-dive/cofhe-components/threshold-network.mdx +++ b/deep-dive/cofhe-components/threshold-network.mdx @@ -39,3 +39,36 @@ The coordinator splits the CT into individual Learning With Errors (LWE) CT bloc The MPC protocol consists of multiple stages. In each stage, a partymember performs a calculation on a received input and returns the result (a.k.a. intermediate result) to the Coordinator. Each intermediate result gets sent back to the coordinator in order to get distributed among other partymembers as an input for the next stage. +## Dispatcher Signing + +The Threshold Network's **Dispatcher** component signs every decrypt and sealoutput result with an ECDSA key. This signature enables on-chain verification — clients can publish signed decrypt results directly to the TaskManager contract via `FHE.publishDecryptResult()`. + +### Signed Message Format + +For decrypt results, the Dispatcher produces a fixed **76-byte** message before signing: + +| Field | Size | Encoding | +|-------|------|----------| +| `result` | 32 bytes | uint256, big-endian, left-padded with zeros | +| `enc_type` | 4 bytes | i32, big-endian | +| `chain_id` | 8 bytes | u64, big-endian | +| `ct_hash` | 32 bytes | uint256, big-endian | + +This format is aligned with Solidity types so the TaskManager can reconstruct and verify the same hash on-chain using `_computeDecryptResultHash`. + +### Signature V Format + +The ECDSA recovery ID (`v` value) can be returned in two formats, controlled by the HTTP header `X-Signature-V-Format`: + +| Header Value | V Format | Use Case | +|-------------|----------|----------| +| `"raw"` (default) | 0-3 | General purpose, k256 native | +| `"evm"` | 27-28 | Direct use with Solidity's `ecrecover` / OpenZeppelin `ECDSA.recover` | + + +For on-chain verification via `FHE.publishDecryptResult()`, use `"evm"` format so the signature is directly compatible with the TaskManager's ECDSA verification. + + +### Signer Registration + +The Dispatcher's signing key address is registered on-chain as `decryptResultSigner` in the TaskManager contract. Only results signed by this address are accepted. Setting it to `address(0)` disables verification (debug mode only). diff --git a/deep-dive/data-flows/decryption-request-flow.mdx b/deep-dive/data-flows/decryption-request-flow.mdx index f501bf1..89c2fa0 100644 --- a/deep-dive/data-flows/decryption-request-flow.mdx +++ b/deep-dive/data-flows/decryption-request-flow.mdx @@ -55,11 +55,21 @@ The Threshold Network performs secure decryption: - Perform secure decryption - -After decryption is complete: 7️⃣ + +After decryption is complete, the result can reach the chain via two paths: 7️⃣ -- Call appropriate callback function on the Result Processor -- The Result Processor publishes the result back to the TaskManager on the host chain +**Path A — Result Processor (default):** +- FheOS calls the Result Processor with the decrypt result +- The Result Processor publishes the result to the TaskManager on the host chain + +**Path B — Client-Published with Signature:** +- The Dispatcher returns the decrypt result along with an **ECDSA signature** to the client (via cofhejs HTTP) +- The client (or any relayer) calls `FHE.publishDecryptResult(ctHash, result, signature)` on-chain +- The TaskManager verifies the signature against the registered `decryptResultSigner` before storing the result + + +Path B enables permissionless result delivery — anyone holding a valid signature can publish. This is useful for client-driven settlement or relayer patterns. + @@ -67,6 +77,7 @@ The TaskManager finalizes the decryption process: 8️⃣ - Provide decrypted result by emitting an event `DecryptionResult` - The event consists of `ciphertext handle`, `result`, `requestor` (of that decrypt operation) +- In Path B, `requestor` is `msg.sender` (the publisher), not the original decrypt requester diff --git a/deep-dive/data-flows/decrypt-from-cofhejs.mdx b/deep-dive/data-flows/off-chain-decryption-flow.mdx similarity index 100% rename from deep-dive/data-flows/decrypt-from-cofhejs.mdx rename to deep-dive/data-flows/off-chain-decryption-flow.mdx diff --git a/docs.json b/docs.json index 4e1f245..d0a494f 100644 --- a/docs.json +++ b/docs.json @@ -211,7 +211,7 @@ "deep-dive/data-flows/encryption-request-flow", "deep-dive/data-flows/fhe-operation-request-flow", "deep-dive/data-flows/decryption-request-flow", - "deep-dive/data-flows/decrypt-from-cofhejs" + "deep-dive/data-flows/off-chain-decryption-flow" ] }, { diff --git a/fhe-library/core-concepts/decryption-operations.mdx b/fhe-library/core-concepts/decryption-operations.mdx index 6a0232d..15b80f1 100644 --- a/fhe-library/core-concepts/decryption-operations.mdx +++ b/fhe-library/core-concepts/decryption-operations.mdx @@ -42,12 +42,23 @@ The client calls `decryptForView` off-chain to obtain the plaintext for display Use `decryptForTx` when you need to act on the decrypted value in a smart contract. Use `decryptForView` when you only need to display the value in a UI. +### 3. Client-Published Decryption (Signature-Verified) + +The client decrypts off-chain via `decryptForTx`, receives the plaintext result along with an **ECDSA signature** from the Threshold Network's Dispatcher, and then publishes the result on-chain by calling `FHE.publishDecryptResult()`. The TaskManager verifies the signature on-chain before storing the result. + +This combines the best of both worlds: the client controls when the result lands on-chain, while the contract can still use the decrypted value. + + +The signature cryptographically proves the result came from the authorized Threshold Network. No trust in the publisher is required — anyone holding a valid signature can submit it. + + ### Comparison Table | Method | Visibility | Gas Cost | Smart Contract Usable | Best For | |--------|-----------|----------|----------------------|----------| | **`decryptForTx`** | Public (once published on-chain) | Gas for the publish/verify tx | Yes | Public results, contract logic | | **`decryptForView`** | Private (off-chain only) | None | No | UI display, confidential data | +| **Client-Published (signature)** | Public (on-chain) | Medium | Yes | Client-driven settlement, permissionless delivery | --- @@ -237,6 +248,33 @@ await tx.wait(); --- +## Batch Client-Published Decryption + +```solidity +function publishMultipleResults( + uint256[] memory ctHashes, + uint256[] memory results, + bytes[] memory signatures +) external { + FHE.publishDecryptResultBatch(ctHashes, results, signatures); +} +``` + +--- + +## Signature Verification Functions + +When using client-published decryption, two verification functions are available: + +| Function | Behavior on Invalid Signature | +|----------|-------------------------------| +| `FHE.verifyDecryptResult(ctHash, result, signature)` | Reverts | +| `FHE.verifyDecryptResultSafe(ctHash, result, signature)` | Returns `false` | + +Both functions accept type-specific overloads for `ebool`, `euint8`, `euint16`, `euint32`, `euint64`, `euint128`, and `eaddress`. + +--- + ## Best Practices diff --git a/fhe-library/reference/fhe-sol.mdx b/fhe-library/reference/fhe-sol.mdx index 5d23280..89428b2 100644 --- a/fhe-library/reference/fhe-sol.mdx +++ b/fhe-library/reference/fhe-sol.mdx @@ -957,59 +957,101 @@ if (decrypted) { } ``` +## Decrypt Result Publishing & Verification + ### publishDecryptResult -Publishes a decrypted result on-chain by verifying the Threshold Network signature. The plaintext is stored on-chain and can be read by anyone. Use this when the decrypted value should be publicly visible after verification. +Publishes a signed decrypt result from the Threshold Network to the chain. The TaskManager verifies the ECDSA signature before storing the result. Anyone holding a valid signature can call this. - -Ciphertext handle of the encrypted value that was decrypted off-chain + +The ciphertext hash to publish a result for - -The decrypted plaintext value returned by `decryptForTx` + +The decrypted plaintext value (type matches the ctHash type) -The Threshold Network signature proving the plaintext is authentic +The ECDSA signature from the Threshold Network's Dispatcher - -The transaction reverts if the signature is invalid. The encrypted value must have been granted public access via `allowPublic` before decryption was requested off-chain. - - ```solidity -// Publish the decrypted result — plaintext is stored on-chain -FHE.publishDecryptResult(encryptedBid, bidPlaintext, bidSignature); +// Publish a decrypt result for a euint64 +FHE.publishDecryptResult(myEncryptedValue, uint64(42), signature); -// The value is now publicly readable +// Publish for an ebool +FHE.publishDecryptResult(myEncryptedBool, true, signature); ``` ### verifyDecryptResult -Verifies a Threshold Network signature for a decrypted value without storing the plaintext on-chain. Use this when you need to act on the decrypted value within the transaction but don't want to persist it publicly. +Verifies a decrypt result signature without publishing. Reverts if the signature is invalid. - -Ciphertext handle of the encrypted value that was decrypted off-chain + +The ciphertext hash - -The decrypted plaintext value returned by `decryptForTx` + +The decrypted plaintext value -The Threshold Network signature proving the plaintext is authentic +The ECDSA signature to verify - -Use `verifyDecryptResult` instead of `publishDecryptResult` when you only need to use the decrypted value within the transaction logic without making it permanently public. - + +Returns `true` if the signature is valid. Reverts otherwise. + + +```solidity +bool valid = FHE.verifyDecryptResult(myEncryptedValue, uint64(42), signature); +``` + +### publishDecryptResultBatch + +Publishes multiple signed decrypt results in a single transaction for gas efficiency. + + +Array of ciphertext hashes + + + +Array of decrypted plaintext values + + + +Array of ECDSA signatures (one per result) + ```solidity -// Verify the proof and use the value — plaintext is NOT stored on-chain -FHE.verifyDecryptResult(encryptedAmount, amount, signature); +FHE.publishDecryptResultBatch(ctHashes, results, signatures); +``` + +### verifyDecryptResultSafe + +Same as `verifyDecryptResult`, but returns `false` instead of reverting on invalid signatures. + + +The ciphertext hash + + + +The decrypted plaintext value + + + +The ECDSA signature to verify + -// Use the verified plaintext in transaction logic -require(amount >= minThreshold, "Below minimum"); + +Returns `true` if valid, `false` if invalid (never reverts on bad signature) + + +```solidity +bool valid = FHE.verifyDecryptResultSafe(myEncryptedValue, uint64(42), signature); +if (!valid) { + revert("Invalid decrypt result signature"); +} ``` ## Access Control From 20314aa86d1107e768d3482759f3e30cf82b6a15 Mon Sep 17 00:00:00 2001 From: Alexandre Carvalheira Date: Wed, 25 Mar 2026 23:15:51 -0300 Subject: [PATCH 2/3] fhe-sol new version --- fhe-library/reference/fhe-sol.mdx | 279 +++++++++++++++------ get-started/introduction/compatibility.mdx | 4 +- 2 files changed, 207 insertions(+), 76 deletions(-) diff --git a/fhe-library/reference/fhe-sol.mdx b/fhe-library/reference/fhe-sol.mdx index 2bad283..e46587e 100644 --- a/fhe-library/reference/fhe-sol.mdx +++ b/fhe-library/reference/fhe-sol.mdx @@ -8,7 +8,7 @@ description: "Complete API reference for the FHE library - encrypted data types, The FHE library provides a comprehensive set of functions for working with Fully Homomorphic Encryption (FHE) in Solidity smart contracts. This library enables you to perform computations on encrypted data without decrypting it, ensuring privacy throughout your contract's execution. -All functions in the FHE library are prefixed with `FHE.` when called. For example: `FHE.add(a, b)` or `FHE.decrypt(value)`. +All functions in the FHE library are prefixed with `FHE.` when called. For example: `FHE.add(a, b)` or `FHE.allowPublic(value)`. ## Encrypted Data Types @@ -870,53 +870,11 @@ ebool result = FHE.select(condition, trueValue, falseValue); eaddress result = FHE.select(condition, address1, address2); ``` -## Encryption and Decryption - -### encrypt - -Encrypts a plaintext value to its corresponding encrypted type. - - -Plaintext value to encrypt - - - -Encrypted value corresponding to the input type - - -```solidity -ebool encrypted = FHE.encrypt(true); -euint8 encrypted = FHE.encrypt(uint8(42)); -euint32 encrypted = FHE.encrypt(uint32(1000)); -eaddress encrypted = FHE.encrypt(0x1234...); -``` - -### decrypt - -Decrypts an encrypted value. The caller must have permission to access the encrypted value. - - -Encrypted value to decrypt - - - -Decrypted plaintext value - - - -Decryption reveals the plaintext value. Use with caution and ensure proper access control is in place. - - -```solidity -bool decrypted = FHE.decrypt(encryptedBool); -uint8 decrypted = FHE.decrypt(encryptedUint8); -uint32 decrypted = FHE.decrypt(encryptedUint32); -address decrypted = FHE.decrypt(encryptedAddress); -``` +## Decryption Results ### getDecryptResult -Retrieves the decrypted result of a previously decrypted value. This function should be called after requesting decryption with `decrypt()`. +Retrieves a published decryption result. This function should be called after a result has been published via `publishDecryptResult`. Hash of the encrypted value that was previously decrypted @@ -1012,6 +970,118 @@ FHE.verifyDecryptResult(encryptedAmount, amount, signature); require(amount >= minThreshold, "Below minimum"); ``` +### verifyDecryptResultSafe + +Like `verifyDecryptResult`, but returns `false` instead of reverting if the signature is invalid. + + +Ciphertext handle of the encrypted value + + + +The decrypted plaintext value + + + +The Threshold Network signature + + + +True if the signature is valid, false otherwise (does not revert) + + +```solidity +bool valid = FHE.verifyDecryptResultSafe(encryptedAmount, amount, signature); +if (valid) { + // proceed with verified plaintext +} +``` + +### publishDecryptResultBatch + +Publishes multiple decrypted results on-chain in a single call. Each element is verified independently. + + +Array of ciphertext handles + + + +Array of decrypted plaintext values (must match ctHashes length and type) + + + +Array of Threshold Network signatures (must match ctHashes length) + + +```solidity +euint64[] memory handles = new euint64[](2); +handles[0] = encryptedBid1; +handles[1] = encryptedBid2; + +uint64[] memory values = new uint64[](2); +values[0] = bid1Plaintext; +values[1] = bid2Plaintext; + +bytes[] memory sigs = new bytes[](2); +sigs[0] = sig1; +sigs[1] = sig2; + +FHE.publishDecryptResultBatch(handles, values, sigs); +``` + +### verifyDecryptResultBatch + +Verifies multiple Threshold Network signatures in a single call without storing results. Reverts if any signature is invalid. + + +Array of ciphertext handles + + + +Array of decrypted plaintext values + + + +Array of Threshold Network signatures + + + +True if all signatures are valid (reverts otherwise) + + +```solidity +bool allValid = FHE.verifyDecryptResultBatch(handles, values, sigs); +``` + +### verifyDecryptResultBatchSafe + +Like `verifyDecryptResultBatch`, but returns a `bool[]` indicating which entries are valid instead of reverting. + + +Array of ciphertext handles + + + +Array of decrypted plaintext values + + + +Array of Threshold Network signatures + + + +Array of booleans — `true` for each valid signature, `false` for invalid + + +```solidity +bool[] memory results = FHE.verifyDecryptResultBatchSafe(handles, values, sigs); +for (uint i = 0; i < results.length; i++) { + if (results[i]) { + // entry i is valid + } +} +``` + ## Access Control ### allow @@ -1049,18 +1119,22 @@ FHE.allowThis(counter); // Required for future access ### allowPublic -Grants public permission to access the encrypted value. +Grants public permission to access the encrypted value. Once called, anyone can request decryption of this value off-chain via `decryptForTx` without needing a permit. Encrypted value to grant public access to -Use `allowPublic` with caution. It grants access to all accounts and contracts. +Once `allowPublic` is called, the value can be decrypted by anyone. Only use this when you intend to reveal the value publicly (e.g., after an auction closes or when unwrapping tokens). ```solidity -FHE.allowPublic(encryptedValue); +// Allow anyone to decrypt the winning bid after auction closes +FHE.allowPublic(highestBid); +FHE.allowPublic(highestBidder); + +// Now anyone can call decryptForTx off-chain without a permit ``` ### allowSender @@ -1075,26 +1149,6 @@ Encrypted value to grant access to FHE.allowSender(encryptedValue); ``` -### allowPublic - -Marks an encrypted value as eligible for public decryption. Once called, anyone can request decryption of this value off-chain via `decryptForTx` without needing a permit. - - -Encrypted value to allow public decryption for - - - -Once `allowPublic` is called, the value can be decrypted by anyone. Only use this when you intend to reveal the value publicly (e.g., after an auction closes or when unwrapping tokens). - - -```solidity -// Allow anyone to decrypt the winning bid after auction closes -FHE.allowPublic(highestBid); -FHE.allowPublic(highestBidder); - -// Now anyone can call decryptForTx off-chain without a permit -``` - ### allowTransient Grants temporary permission to the specified account to access the encrypted value. @@ -1131,6 +1185,76 @@ True if the account has permission, false otherwise bool hasAccess = FHE.isAllowed(encryptedValue, userAddress); ``` +### isPubliclyAllowed + +Checks if the encrypted value has been granted public access via `allowPublic`. + + +Encrypted value to check + + + +True if the value is publicly accessible, false otherwise + + +```solidity +bool isPublic = FHE.isPubliclyAllowed(encryptedValue); +``` + +## Utility Functions + +### isInitialized + +Checks whether an encrypted value has been initialized (i.e., is not a zero/empty handle). + + +Encrypted value to check + + + +True if the value has been initialized, false otherwise + + +```solidity +if (FHE.isInitialized(encryptedBalance)) { + // safe to use +} +``` + +### unwrap + +Extracts the raw `bytes32` handle from a typed encrypted value. + + +Encrypted value to unwrap + + + +The underlying ciphertext handle + + +```solidity +bytes32 handle = FHE.unwrap(encryptedValue); +``` + +### wrapEbool / wrapEuint8 / wrapEuint16 / wrapEuint32 / wrapEuint64 / wrapEuint128 / wrapEaddress + +Wraps a raw `bytes32` handle into a typed encrypted value. Use this when you have a raw handle (e.g., from storage or an event) and need to convert it to a typed encrypted value. + + +Raw ciphertext handle to wrap + + +```solidity +ebool val = FHE.wrapEbool(handle); +euint8 val = FHE.wrapEuint8(handle); +euint16 val = FHE.wrapEuint16(handle); +euint32 val = FHE.wrapEuint32(handle); +euint64 val = FHE.wrapEuint64(handle); +euint128 val = FHE.wrapEuint128(handle); +eaddress val = FHE.wrapEaddress(handle); +``` + ## Bindings The FHE library provides binding libraries that enable syntactic sugar for working with encrypted types. These bindings allow for more intuitive and object-oriented usage patterns. @@ -1196,8 +1320,11 @@ euint32 toU32 = a.toU32(); // Convert to euint32 euint64 toU64 = a.toU64(); // Convert to euint64 euint128 toU128 = a.toU128(); // Convert to euint128 +// Utility +bool initialized = a.isInitialized(); // Check if initialized +bytes32 handle = a.unwrap(); // Get raw handle + // Access control -a.decrypt(); // Decrypt a.allow(address); // Allow access a.allowThis(); // Allow this contract a.allowPublic(); // Allow public access @@ -1214,7 +1341,7 @@ Always consider security implications when working with encrypted data. 1. **Initialization**: All FHE functions check if their inputs are initialized and set them to 0 if not. -2. **Decryption**: Decryption functions reveal the plaintext values and should be used with caution. Only decrypt when absolutely necessary. +2. **Decryption**: Decryption is a two-phase process — mark values with `allowPublic` on-chain, decrypt off-chain via the Client SDK, then publish/verify the result with `publishDecryptResult` or `verifyDecryptResult`. Only reveal values when absolutely necessary. 3. **Security Zones**: Some functions accept a `securityZone` parameter to isolate different encrypted computations. FHE operations can only be performed between ciphertexts that share the same security zone. @@ -1244,9 +1371,13 @@ ebool isGreater = FHE.gt(b, a); // Encrypted comparison // Conditional logic euint8 result = FHE.select(isGreater, sum, product); -// Grant access and decrypt the result +// Grant access for future use and allow public decryption FHE.allowThis(result); -uint8 decryptedResult = FHE.decrypt(result); +FHE.allowPublic(result); + +// Off-chain: client calls decryptForTx(ctHash) to get plaintext + signature +// On-chain: publish the verified result +// FHE.publishDecryptResult(result, plaintext, signature); ``` ### Example with Bindings @@ -1267,9 +1398,9 @@ ebool isGreater = b.gt(a); // Encrypted comparison // Conditional logic euint8 result = isGreater.select(sum, product); -// Grant access and decrypt the result +// Grant access for future use and allow public decryption result.allowThis(); -uint8 decryptedResult = FHE.decrypt(result); +result.allowPublic(); ``` ## Next Steps diff --git a/get-started/introduction/compatibility.mdx b/get-started/introduction/compatibility.mdx index 57eac89..c75cbc9 100644 --- a/get-started/introduction/compatibility.mdx +++ b/get-started/introduction/compatibility.mdx @@ -17,7 +17,7 @@ The following table lists all core CoFHE components with their current and minim | Component | Current Version | Minimum Compatible Version | Notes | |-----------|----------------|----------------------------|-------| -| **@fhenixprotocol/cofhe-contracts** | [`0.1.0`](https://github.com/FhenixProtocol/cofhe-contracts/tree/v0.1.0) | `0.1.0` | Solidity libraries and smart contracts for FHE operations | +| **@fhenixprotocol/cofhe-contracts** | [`0.1.3`](https://github.com/FhenixProtocol/cofhe-contracts/tree/v0.1.3) | `0.1.3` | Solidity libraries and smart contracts for FHE operations | | **@cofhe/sdk** | [`0.4.0`](https://github.com/FhenixProtocol/cofhesdk/releases/tag/v0.4.0) | `0.4.0` | JavaScript library for interacting with FHE contracts and the CoFHE coprocessor | | **@cofhe/hardhat-plugin** | [`0.4.0`](https://github.com/FhenixProtocol/cofhe-hardhat-plugin/releases/tag/v0.4.0) | `0.4.0` | Hardhat plugin that deploys mock contracts and exposes utilities | | **@cofhe/mock-contracts** | `0.4.0` | `0.4.0` | Mock contracts for local development and testing | @@ -29,7 +29,7 @@ These packages provide shared utilities and higher-level abstractions used acros | Component | Current Version | Minimum Compatible Version | Notes | |-----------|----------------|----------------------------|-------| | **@fhenixprotocol/cofhe-errors** | `1.0.2` | `1.0.2` | Shared error definitions for CoFHE contracts | -| **@fhenixprotocol/fhenix-confidential-contracts** | `1.0.2` | `1.0.2` | Pre-built confidential contract primitives (FHERC20, etc.) | +| **@fhenixprotocol/fhenix-confidential-contracts** | `0.2.1` | `0.2.1` | Pre-built confidential contract primitives (FHERC20, etc.) | ### Version Compatibility Guidelines From d5a9b5488116e7bbb894136f51f364bd35a74e30 Mon Sep 17 00:00:00 2001 From: Alexandre Carvalheira Date: Wed, 25 Mar 2026 23:31:54 -0300 Subject: [PATCH 3/3] fix fhe.require and remove publishing result to the TaskManager --- .../data-flows/decryption-request-flow.mdx | 25 +++++-------------- fhe-library/core-concepts/require.mdx | 5 ++-- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/deep-dive/data-flows/decryption-request-flow.mdx b/deep-dive/data-flows/decryption-request-flow.mdx index 89c2fa0..f10bf11 100644 --- a/deep-dive/data-flows/decryption-request-flow.mdx +++ b/deep-dive/data-flows/decryption-request-flow.mdx @@ -55,29 +55,16 @@ The Threshold Network performs secure decryption: - Perform secure decryption - -After decryption is complete, the result can reach the chain via two paths: 7️⃣ + +After decryption is complete: 7️⃣ -**Path A — Result Processor (default):** -- FheOS calls the Result Processor with the decrypt result -- The Result Processor publishes the result to the TaskManager on the host chain - -**Path B — Client-Published with Signature:** -- The Dispatcher returns the decrypt result along with an **ECDSA signature** to the client (via cofhejs HTTP) -- The client (or any relayer) calls `FHE.publishDecryptResult(ctHash, result, signature)` on-chain -- The TaskManager verifies the signature against the registered `decryptResultSigner` before storing the result +- The Threshold Network returns the plaintext along with an **ECDSA signature** to the client (via the Client SDK) +- The client (or any relayer) calls `FHE.publishDecryptResult(ctHash, result, signature)` or `FHE.verifyDecryptResult(ctHash, result, signature)` on-chain +- The on-chain contract verifies the signature before accepting the result -Path B enables permissionless result delivery — anyone holding a valid signature can publish. This is useful for client-driven settlement or relayer patterns. +This enables permissionless result delivery — anyone holding a valid signature can publish. This is useful for client-driven settlement or relayer patterns. - - -The TaskManager finalizes the decryption process: 8️⃣ - -- Provide decrypted result by emitting an event `DecryptionResult` -- The event consists of `ciphertext handle`, `result`, `requestor` (of that decrypt operation) -- In Path B, `requestor` is `msg.sender` (the publisher), not the original decrypt requester - diff --git a/fhe-library/core-concepts/require.mdx b/fhe-library/core-concepts/require.mdx index 83a445e..3e312b9 100644 --- a/fhe-library/core-concepts/require.mdx +++ b/fhe-library/core-concepts/require.mdx @@ -43,8 +43,9 @@ require(msg.sender == owner, "Not authorized"); // Good - use select for encrypted logic euint32 result = FHE.select(encryptedCondition, valueA, valueB); -// Bad - require on encrypted condition leaks information! -require(FHE.decrypt(encryptedValue.gt(threshold)), "Value too low"); +// Bad - require on encrypted condition is not possible! +// FHE.decrypt was removed — decryption is now off-chain only +// require(FHE.decrypt(encryptedValue.gt(threshold)), "Value too low"); ``` ---