diff --git a/.DS_Store b/.DS_Store index bab4cde..474be5d 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/STYLE.md b/STYLE.md index 9b08763..de7bc96 100644 --- a/STYLE.md +++ b/STYLE.md @@ -63,8 +63,7 @@ One name per thing, used consistently. Canonical names: |---|---| | CoFHE | The coprocessor as a whole. Not COFHE, not Cofhe. | | Teecryptor | The TEE decryption service. Capitalized as a product name. | -| FHE Engine | The service that executes FHE operations. | -| FheOS Server | The service that verifies and queues incoming work. It does not execute FHE operations. | +| FHE Engine | The offchain pipeline that subscribes to task events, executes FHE operations, and posts commitments. One name for the whole pipeline; its internal stages are not named in public docs. | | ZK Verifier | The input proof verification service. | | TaskManager, CommitmentRegistry, ACL | Contract names, written as in the source. | | ACP | Access Control Permission. Replaces "Permit" from `0.7` onward, so that it is not confused with an ERC-2612 permit. Spell it out before the acronym: a page whose subject is ACPs carries the full term in its `title`, and every other page expands it on first use. Never write "ACP permission". | @@ -100,6 +99,7 @@ Do not use internal names in public docs: no hostnames, no GCP project names, no - Internal links use root-relative paths: `/fhe-library/core-concepts/access-control`, not `../core-concepts/access-control` and not the full `https://cofhe-docs.fhenix.zone/...` URL. Relative paths break when a page moves; absolute URLs break preview deployments. - Link text describes the destination. "See [access control](/fhe-library/core-concepts/access-control)", never `"click [here](/...)"` or a bare URL. +- Never link to a private repository; readers get a 404. Of the FhenixProtocol repos, only `cofhe-contracts` and `cofhesdk` are public. Name a component or path in prose instead, and add the link when the repo goes public. - Every image is wrapped in `` and carries alt text that says what the image shows, not what it is called. "Sealed output flowing from the FHE Engine to the client", not "diagram". - Pick the component that matches the content, and use each one for one job: @@ -119,6 +119,8 @@ Do not use internal names in public docs: no hostnames, no GCP project names, no A page full of callouts has none. +- Long code identifiers in narrow table columns get chopped mid-word by the table layout. Keep the first column to bare names (no argument lists) and guard each one with `name`. Full signatures belong in prose above the table or in the description column. + ## Code samples - Every sample must compile or run against the currently published versions. If it would not run when pasted, it does not ship. diff --git a/deep-dive/cofhe-components/acl.mdx b/deep-dive/cofhe-components/acl.mdx index 140b31f..6281607 100644 --- a/deep-dive/cofhe-components/acl.mdx +++ b/deep-dive/cofhe-components/acl.mdx @@ -1,11 +1,48 @@ --- title: ACL (Access Control Layer) -description: "On-chain contract that manages and enforces access control for ciphertexts, ensuring only authorized contracts can reference or decrypt them" +description: "Onchain contract that manages and enforces access control for ciphertexts, ensuring only authorized contracts and accounts can reference or decrypt them" --- | Aspect | Description | |--------|-------------| -| **Type** | Contract deployed on the destination blockchain | -| **Function** | Manages and enforces access control for ciphertexts, ensuring only authorized contracts can reference or decrypt them. | -| **Responsibilities** | An internal contract that is responsible for managing and verifying access for each and every ciphertext. | +| **Type** | Contract deployed on the host chain, UUPS-upgradeable behind a proxy. | +| **Function** | Manages and enforces access control for ciphertexts, for both contracts and user accounts. | +| **Responsibilities** | • Records who may use each handle in FHE operations.
• Records which handles may be decrypted, and by whom.
• Answers the access queries of the [TaskManager](/deep-dive/cofhe-components/task-manager) and the decryption service. | +Every encrypted value in CoFHE is guarded by this contract. A handle is useless to anyone the ACL does not list. The TaskManager rejects operations on inputs the caller cannot access, and [Teecryptor](/deep-dive/cofhe-components/teecryptor) refuses to decrypt handles without a matching grant. + +## Grant tiers + +| Tier | Granted by | Scope | +|------|------------|-------| +| **Transient** | `FHE.allowTransient` | Current transaction only. Stored with EIP-1153 transient storage, so it costs no persistent state. | +| **Persistent** | `FHE.allow(handle, account)` | A specific account (contract or EOA), permanently. | +| **Global** | `FHE.allowGlobal` (alias `FHE.allowPublic`) | Every account. Also marks the handle publicly decryptable. | +| **Decryption** | `TaskManager.allowForDecryption` | Adds the handle to the decryption allowlist and emits `AllowedForDecryption`. | + +## Writes go through the TaskManager + +All state-mutating entry points require `msg.sender` to be the TaskManager; direct calls revert with `DirectAllowForbidden`. Contracts grant access through the `FHE.sol` helpers (`allow`, `allowThis`, `allowSender`, `allowGlobal`, `allowTransient`), which route through the TaskManager. + +## Permits + +Offchain reads are authorized by a [permit](/client-sdk/guides/permits), which onchain is an `ACP` (Access Control Permission): an EIP-712 body signed by its issuer. Beyond the issuer, expiration, and the sealing key used for sealed outputs, an ACP carries a **scope**: global, limited to specific contracts, or limited to specific handles. A scope only narrows what the issuer could already access; it never grants more. + +An ACP can be shared with a recipient, revoked through a revoker contract, and handed over onchain through the ACP share registry. + +## Read surface + +| Function | Answers | +|----------|---------| +| `isAllowed(handle, account)` | May this account use the handle? (any tier) | +| `allowedTransient(handle, account)` | Is there a transient grant in this transaction? | +| `persistAllowed(handle, account)` | Is there a persistent grant? | +| `globalAllowed(handle)` | Is the handle globally allowed? | +| `isAllowedForDecryption(handle)` | Is the handle on the decryption allowlist? | +| isAllowedWithPermission | Does this permit (ACP) authorize its issuer for the given handle? | + +The last two are what the decryption path runs on. For every `decrypt` or `sealoutput` request, Teecryptor queries the ACL through the TaskManager: `isAllowedWithPermission` when a permit is attached, or `isPubliclyAllowed` (the TaskManager's wrapper over `globalAllowed`) when none is. A future [Threshold Network](/deep-dive/research/future-plans) will consume the same interface. + +## Upgrades + +The contract is UUPS-upgradeable behind a proxy, and storage uses ERC-7201 namespaced slots for upgrade safety. diff --git a/deep-dive/cofhe-components/commitment-registry.mdx b/deep-dive/cofhe-components/commitment-registry.mdx index 9b22146..1dce5e0 100644 --- a/deep-dive/cofhe-components/commitment-registry.mdx +++ b/deep-dive/cofhe-components/commitment-registry.mdx @@ -1,20 +1,24 @@ --- title: CommitmentRegistry -description: "Registry-chain contract that records FHE computation commitments. The Threshold Network reads it to verify ciphertext integrity before decrypting." +description: "Registry-chain contract that records FHE computation commitments. Teecryptor verifies ciphertext integrity against it before decrypting." --- | Aspect | Description | |--------|-------------| -| **Type** | UUPS-upgradeable Solidity contract deployed on a **registry chain** (Arbitrum One in production). Distinct from the per-host-chain [CTRegistry](/deep-dive/cofhe-components/ct-registry). | -| **Function** | Records `(version, handle) → commitHash` entries for every FHE operation result that the coprocessor produces. | -| **Responsibilities** | • Provide an authoritative source of ciphertext integrity that the Threshold Network checks **before** issuing a decryption.
• Group commitments by an opaque `version` tag so a future tfhe-rs / FHE-parameter upgrade can roll out without invalidating earlier ciphertexts.
• Enforce write-once semantics per `(version, handle)` to prevent commitment replacement.
• Expose paginated enumeration so off-chain tooling can audit what has been posted. | -| **Deployment** | One deployment per registry chain, behind an ERC-1967 proxy. Initialized with `(initialOwner, initialPoster)`. Owner is `Ownable2Step` — transfers require explicit accept. | +| **Type** | UUPS-upgradeable Solidity contract deployed on a dedicated **registry chain**. | +| **Function** | Records `(version, handle) → commitHash` entries for every ciphertext the coprocessor produces or verifies, computed results and encrypted inputs alike. | +| **Responsibilities** | • Provide an authoritative source of ciphertext integrity that [Teecryptor](/deep-dive/cofhe-components/teecryptor) checks **before** decrypting.
• Group commitments by an opaque `version` tag so a future tfhe-rs / FHE-parameter upgrade can roll out without invalidating earlier ciphertexts.
• Enforce write-once semantics per `(version, handle)` to prevent commitment replacement.
• Expose paginated enumeration so offchain tooling can audit what has been posted. | +| **Deployment** | One deployment per registry chain, behind an ERC-1967 proxy. | -## Why a separate registry chain? +## What a commitment is -The Threshold Network needs to confirm that the ciphertext it's about to decrypt is **exactly** the one the FHE Engine produced (not a tampered or stale handle). The natural place to anchor that proof is on-chain, but doing it on every host chain would force the network to maintain N RPC paths and pay gas on N chains for every FHE operation. Instead, the coprocessor posts commitments to a **single registry chain** (currently Arbitrum One), and the Threshold Network only watches that one. +A commitment is the `keccak256` hash of a ciphertext's canonical stored bytes. The [FHE Engine](/deep-dive/cofhe-components/fhe-engine) produces one for every result it computes, and the coprocessor anchors one for every encrypted input it verifies. The engine batches them and writes them to this registry. -This is why the host-chain [CTRegistry](/deep-dive/cofhe-components/ct-registry) (which maps temporary → final ciphertext hashes inside one chain's lifecycle) and `CommitmentRegistry` (which records the canonical commitment for every produced ciphertext, cross-chain) are deliberately distinct components. +## Why commitments? + +The commitment is a safety check in the decryption flow. Before decrypting anything, [Teecryptor](/deep-dive/cofhe-components/teecryptor) confirms that the ciphertext bytes it fetched hash to the commitment anchored onchain. It only ever decrypts a ciphertext the coprocessor actually produced, never a tampered or substituted one. + +The registry also makes the coprocessor accountable. Every result it has ever produced is committed publicly, permanently, and write-once, so its computation history is tamper-evident and open to independent audit. Commitments for every host chain land on one dedicated **registry chain**, which gives the decryption path a single place to verify against. ## Storage shape @@ -29,32 +33,30 @@ mapping(address => bool) poster ## Version lifecycle -`version` is an opaque `bytes32` tag chosen by the coprocessor when FHE parameters change (see [the FHE Engine `COMMITMENT_VERSION` notes](https://github.com/FhenixProtocol/cofhe/blob/master/CHANGELOG.md#060---2026-05-05)). Every version moves through a small state machine: +`version` is an opaque `bytes32` tag chosen by the coprocessor when FHE parameters change, currently the ASCII tag `"2"`. Every version moves through a small state machine: -``` -Unset ─┐ - ▼ - Active ─┬──────► Deprecated ──► Revoked - └─────────────────────► Revoked +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +stateDiagram-v2 + direction LR + Unset --> Active + Active --> Deprecated + Active --> Revoked + Deprecated --> Revoked ``` | State | Meaning | Allowed transitions | |-------|---------|---------------------| -| `Unset` | Default. No commitments have been posted under this version. | → `Active` | -| `Active` | Posters may write commitments under this version. The Threshold Network honors lookups. | → `Deprecated`, → `Revoked` | -| `Deprecated` | New commitments rejected. Existing lookups still resolve. Used during a parameter rollover. | → `Revoked` | -| `Revoked` | Hard kill. No further transitions; the version is dead. | — (terminal) | +| `Unset` | Default. No commitments have been posted under this version. | to `Active` | +| `Active` | Posters may write commitments under this version. Teecryptor honors lookups. | to `Deprecated` or `Revoked` | +| `Deprecated` | New commitments rejected. Existing lookups still resolve. Used during a parameter rollover. | to `Revoked` | +| `Revoked` | Hard kill. No further transitions; the version is dead. | none (terminal) | -Owner-only `setVersionStatus(version, newStatus)` enforces these transitions and reverts with `InvalidVersionTransition` otherwise. The transition emits `VersionStatusChanged(version, oldStatus, newStatus)`. +The admin-only `setVersionStatus(version, newStatus)` enforces these transitions and reverts with `InvalidVersionTransition` otherwise. The transition emits `VersionStatusChanged(version, oldStatus, newStatus)`. -## Roles and write surface +## Write surface -| Role | How it's set | What it can do | -|------|--------------|----------------| -| **Owner** | `initialize(initialOwner, …)`, then `Ownable2Step` transfer. | `addPoster`, `removePoster`, `setVersionStatus`, `_authorizeUpgrade`. | -| **Poster** | Owner-only `addPoster(address)`. Initial poster supplied to `initialize`. | `postCommitments`, `postCommitmentsSafe`. | - -Non-poster posts revert with `OnlyPosterAllowed(caller)`. In production, the [`blockchain-poster`](https://github.com/FhenixProtocol/cofhe/blob/master/CHANGELOG.md#060---2026-05-05) service holds the only poster role and signs through OpenZeppelin Relayer. +Only accounts holding the **poster** role can write commitments (`postCommitments`, `postCommitmentsSafe`); posts from anyone else revert with `OnlyPosterAllowed(caller)`. Poster management and version transitions are admin-only. In production, the poster role is held by the coprocessor's relayer signer (OpenZeppelin Relayer). ## Writing commitments @@ -74,20 +76,20 @@ function postCommitmentsSafe( Both functions batch-write `(version, handle) → commitHash` rows and require: -- `version` is in `Active` state — otherwise reverts with `VersionNotActive(version)`. -- `handles.length == commitHashes.length` and `> 0` — otherwise `LengthMismatch` / `EmptyBatch`. -- Each `commitHash != bytes32(0)` — otherwise `ZeroCommitHash(handle)`. +- `version` is in `Active` state, otherwise the call reverts with `VersionNotActive(version)`. +- `handles.length == commitHashes.length` and `> 0`, otherwise `LengthMismatch` / `EmptyBatch`. +- Each `commitHash != bytes32(0)`, otherwise `ZeroCommitHash(handle)`. The difference is in **how duplicates are handled**: | Function | Duplicate handle under same version | Use case | |----------|-------------------------------------|----------| -| `postCommitments` | Reverts the whole batch with `CommitmentAlreadyExists(version, handle)`. | Strict integrity — caller knows it's posting unique data. | +| `postCommitments` | Reverts the whole batch with `CommitmentAlreadyExists(version, handle)`. | Strict integrity: the caller knows it is posting unique data. | | `postCommitmentsSafe` | Silently skips the handle; emits `CommitmentsPostedSafe(version, newlyPosted, skipped)`. | Idempotent re-flushes (e.g. when the coprocessor's message broker redelivers a commitment batch). | -`postCommitments` emits `CommitmentsPosted(version, batchSize)`. `postCommitmentsSafe` emits `CommitmentsPostedSafe(version, newlyPosted, skipped)` so the off-chain caller can tell whether the round did real work. +`postCommitments` emits `CommitmentsPosted(version, batchSize)`. `postCommitmentsSafe` emits `CommitmentsPostedSafe(version, newlyPosted, skipped)` so the offchain caller can tell whether the round did real work. -Both enforce **write-once per (version, handle)** — a commitment can never be overwritten, only superseded by writing the same handle under a new `version`. +Both enforce **write-once per (version, handle)**: a commitment can never be overwritten, only superseded by writing the same handle under a new `version`. ## Reading commitments @@ -96,27 +98,21 @@ Both enforce **write-once per (version, handle)** — a commitment can never be | `getCommitment(version, handle)` | `bytes32` | `bytes32(0)` means "not posted". | | `getVersionStatus(version)` | `VersionStatus` | `Unset` if never registered. | | `getSize(version)` | `uint256` | Number of handles ever committed under `version`. | -| `getHandleByIndex(version, index)` | `bytes32` | Direct array lookup. Reverts on out-of-range. | -| `getHandles(version, offset, limit)` | `bytes32[]` | Paginated. Returns an empty array if `offset >= total`; clamps `offset + limit` at `total`. | -| `isPoster(address)` | `bool` | Useful for off-chain ops dashboards. | +| getHandleByIndex(version, index) | `bytes32` | Direct array lookup. Reverts on out-of-range. | +| getHandles(version, offset, limit) | `bytes32[]` | Paginated. Returns an empty array if `offset >= total`; clamps `offset + limit` at `total`. | +| `isPoster(address)` | `bool` | Useful for offchain ops dashboards. | -The paginated `getHandles` is the recommended way to enumerate a version — `getSize` first to compute pages, then `getHandles(version, offset, pageSize)` in a loop. +The paginated `getHandles` is the recommended way to enumerate a version: `getSize` first to compute pages, then `getHandles(version, offset, pageSize)` in a loop. ## Events | Event | Emitted by | Use | |-------|-----------|-----| -| `CommitmentsPosted(bytes32 indexed version, uint256 batchSize)` | `postCommitments` | Confirm a strict batch landed. | -| `CommitmentsPostedSafe(bytes32 indexed version, uint256 newlyPosted, uint256 skipped)` | `postCommitmentsSafe` | Reconcile "how many were new" in an idempotent flow. | -| `VersionStatusChanged(bytes32 indexed version, VersionStatus oldStatus, VersionStatus newStatus)` | `setVersionStatus` | Watch for `Active → Deprecated` to know when to stop posting under a version. | -| `PosterAdded(address indexed poster)` / `PosterRemoved(address indexed poster)` | `addPoster` / `removePoster` | Audit role changes. | - -## Upgrades - -The contract is `UUPSUpgradeable`. `_authorizeUpgrade` is gated by `onlyOwner`. The constructor calls `_disableInitializers()` so the implementation contract itself can never be initialized — initialization happens through the proxy via `initialize(initialOwner, initialPoster)`. +| CommitmentsPosted | `postCommitments` | Confirm a strict batch landed. Carries the version and batch size. | +| CommitmentsPostedSafe | `postCommitmentsSafe` | Reconcile "how many were new" in an idempotent flow. Carries newlyPosted and skipped counts. | +| VersionStatusChanged | `setVersionStatus` | Watch for the `Active` to `Deprecated` transition to know when to stop posting under a version. | +| PosterAdded / PosterRemoved | `addPoster` / `removePoster` | Audit role changes. | ## Source - Solidity: [`contracts/internal/registry-chain/contracts/commitment-registry/CommitmentRegistry.sol`](https://github.com/FhenixProtocol/cofhe-contracts/blob/master/contracts/internal/registry-chain/contracts/commitment-registry/CommitmentRegistry.sol). -- Off-chain poster service: [`src/services/blockchain-poster/`](https://github.com/FhenixProtocol/cofhe/tree/master/src/services/blockchain-poster) — introduced in [cofhe `0.6.0`](https://github.com/FhenixProtocol/cofhe/blob/master/CHANGELOG.md#060---2026-05-05). -- FHE Engine commitment-version bumping: [`fhe-engine/src/rabbitmq/handlers.rs`](https://github.com/FhenixProtocol/cofhe/blob/master/fhe-engine/src/rabbitmq/handlers.rs). diff --git a/deep-dive/cofhe-components/ct-registry.mdx b/deep-dive/cofhe-components/ct-registry.mdx deleted file mode 100644 index 74e0721..0000000 --- a/deep-dive/cofhe-components/ct-registry.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: CTRegistry -description: "Registry contract that manages the mapping between temporary ciphertext hashes and their actual hash values, ensuring secure lookup and verification" ---- - -| Aspect | Description | -|--------|-------------| -| **Type** | Registry Contract | -| **Function** | Manages the mapping between temporary ciphertext hashes and their actual hash values | -| **Responsibilities** | • Maintains a consistent record of ciphertext identifiers throughout the CoFHE lifecycle
• Enables secure lookup of final ciphertexts using their temporary handles
• Restricts read/write access to ensure integrity and prevent unauthorized updates | - -The CTRegistry acts as a source of truth for encrypted data identifiers, mapping temporary hashes to their final computed values. This ensures results from off-chain computation can be securely resolved and verified by their originating requests. - diff --git a/deep-dive/cofhe-components/fhe-engine.mdx b/deep-dive/cofhe-components/fhe-engine.mdx new file mode 100644 index 0000000..83162db --- /dev/null +++ b/deep-dive/cofhe-components/fhe-engine.mdx @@ -0,0 +1,40 @@ +--- +title: FHE Engine +description: "The offchain execution pipeline that carries a task from onchain event to committed result" +--- + +| Aspect | Description | +|---------|-------------| +| **Type** | Offchain execution pipeline. | +| **Function** | Carries every FHE task from onchain event to committed result. | +| **Responsibilities** | • Listens to TaskManager events on each host chain
• Validates and orders operations
• Executes them with the TFHE library
• Stores results and posts a commitment for each | + +The FHE Engine is the computation side of CoFHE. Contracts never call it; it subscribes to what happens onchain, does the encrypted math, and anchors the results. From the outside it is one component with four stages. + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +flowchart LR + TM["TaskManager events"] --> L["Listen"] --> V["Validate and order"] --> X["Execute"] --> C["Commit"] --> CR["CommitmentRegistry"] +``` + +## Listen + +The engine watches TaskManager events on every host chain it serves. `TaskCreated` events bring FHE operations in for execution; `InputVerified` events bring verified encrypted inputs in so a commitment gets anchored for each. Delivery is reliable by construction. The listener tracks the last processed block, so a crash or a missed range is re-scanned rather than skipped. + +## Validate and order + +Before anything executes, the engine checks that each operation is well formed and that the inputs it references exist. Operations can arrive before the inputs they depend on have finished computing. Such operations are deferred and released once the missing results land, so out-of-order arrival never produces a wrong answer. Work that is malformed, or that references inputs that never materialize, is set aside for inspection instead of being silently dropped. + +## Execute + +Validated operations run against the TFHE library: arithmetic, comparison, select, cast, and random generation on encrypted operands. The result ciphertext is stored under the handle the TaskManager issued, and any deferred operations waiting on that handle are released as soon as it lands. + +## Commit + +For every stored result, the engine produces a commitment, the `keccak256` hash of the stored ciphertext bytes. Commitments are batched and posted to the [CommitmentRegistry](/deep-dive/cofhe-components/commitment-registry) on the registry chain. This is the anchor [Teecryptor](/deep-dive/cofhe-components/teecryptor) verifies before decrypting anything: only bytes that hash to a registered commitment ever reach the decryption key. + +## Key material + +The engine computes with the FHE public key material only. Production builds load no decryption key, so a compromised engine can corrupt results (which commitment verification would catch) but cannot read them. Decryption capability exists solely inside [Teecryptor](/deep-dive/cofhe-components/teecryptor)'s attested enclave. + +The engine also serves the public encryption parameters that clients need: the network public key and the CRS (common reference string) used to build encryption proofs. diff --git a/deep-dive/cofhe-components/fheos-server.mdx b/deep-dive/cofhe-components/fheos-server.mdx deleted file mode 100644 index 2c544f4..0000000 --- a/deep-dive/cofhe-components/fheos-server.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: FheOs - Server -description: "Off-chain computational layer that executes FHE operations and manages encrypted computations" ---- - -| Aspect | Description | -|---------|-------------| -| **Type** | Off-chain computational layer. | -| **Function** | Executes FHE operations and manages encrypted computations | -| **Responsibilities** | • Receives the request from the Slim Listener
• Executes the FHE operations
• Calls Result Processor when result is created
• Returns plaintext results when requested (i.e decrypt/seal output), preserving privacy throughout the pipeline | - -The FHE Operating System server manages the execution environment for FHE operations. - diff --git a/deep-dive/cofhe-components/key-management.mdx b/deep-dive/cofhe-components/key-management.mdx new file mode 100644 index 0000000..ed478ff --- /dev/null +++ b/deep-dive/cofhe-components/key-management.mdx @@ -0,0 +1,48 @@ +--- +title: Key Management +description: "How CoFHE's keys are created in an attested ceremony, split among independent partners, and released only to attested enclaves" +--- + +CoFHE's security rests on a small set of keys, and none of them is ever held whole by any person or machine outside an attested enclave. + +| Key | Used by | Purpose | +|-----|---------|---------| +| FHE network key | [Teecryptor](/deep-dive/cofhe-components/teecryptor) | Decrypts ciphertexts inside the enclave | +| Result-signing key | Teecryptor | Signs decrypt results the TaskManager verifies onchain | +| Verifier signing key | [ZK Verifier](/deep-dive/cofhe-components/zk-verifier) | Signs verified input batches | +| Public material | Everyone | The network public key and CRS that clients encrypt against | + +## The ceremony + +The keys are born inside a TEE. A one-shot key-generation service runs in its own hardware-attested enclave and generates the network keyset there. The secret material is split with Shamir secret sharing into six shares; any three reconstruct it (3-of-6). + +Each share goes to one **partner**: an independent custodian with its own share store. A partner's store accepts writes only from the attested ceremony image, so nobody can slip in a substitute share. The public material is published for clients. The ceremony enclave then terminates; the assembled key is never persisted anywhere. + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +sequenceDiagram + participant Ceremony as Keygen ceremony (TEE) + participant Partners as Partners (custodians) + participant Pub as Public material + participant Enclave as Service enclave + + Note over Ceremony: generates the keyset in-enclave,
splits secrets with Shamir (3-of-6) + Ceremony->>Partners: one encrypted share per partner (attested write) + Ceremony->>Pub: publish public material + Note over Ceremony: terminates, whole key
never persisted + Note over Partners,Enclave: later, at every service boot + Enclave->>Partners: attestation of the running image + Note over Partners: each partner verifies independently + Partners-->>Enclave: release share, only to the approved image + Note over Enclave: reconstruct in memory,
zeroize transit material +``` + +## Custody + +At rest, the key exists only as shares held by independent partners. No partner can reconstruct anything alone, and no quorum below the threshold can either. Fhenix operates the services, but it cannot assemble the key outside an attested enclave any more than anyone else can. + +## Release at boot + +When Teecryptor or the ZK Verifier starts, the TEE hardware produces an attestation of the exact code image it is running. Each partner independently verifies that attestation and releases its share only to the approved image digest. The enclave reconstructs the key in memory, zeroizes the transit material, and never persists it. Restarting the service repeats the whole handshake. + +The guarantee this chain yields: from generation through every reconstruction, the key exists only inside hardware-attested enclaves running reviewed code. diff --git a/deep-dive/cofhe-components/overview.mdx b/deep-dive/cofhe-components/overview.mdx index 0d6e878..4988e23 100644 --- a/deep-dive/cofhe-components/overview.mdx +++ b/deep-dive/cofhe-components/overview.mdx @@ -1,55 +1,83 @@ --- title: "CoFHE Architecture Overview" -description: "Comprehensive overview of the CoFHE architecture, components, and data flows for privacy-preserving blockchain computations" +description: "How CoFHE's onchain contracts and offchain services fit together to run FHE computations for any EVM chain" --- -# CoFHE Architecture - - -CoFHE Architecture Diagram - - -*Click on the image to view in full size* - -## System Overview - -CoFHE (Co-processor for Fully Homomorphic Encryption) is designed as a modular, layered architecture that enables privacy-preserving computations on blockchain networks. The system combines on-chain smart contracts with off-chain processing capabilities to deliver secure, efficient fully homomorphic encryption operations. - -### Key Components - -#### User-Facing Utilities - -- **Client SDK** (`@cofhe/sdk`): A TypeScript library that provides client-side functionality for encrypting inputs, managing permits, and decrypting outputs. Serves as the primary interface between applications and the CoFHE ecosystem. - -- **FHE.sol**: The Solidity library that enables smart contracts to perform operations on encrypted data. It exposes a comprehensive API for arithmetic, comparison, and logical operations on encrypted values. - -#### Internal Utilities - -- **Task Manager**: Acts as the gateway for all FHE operation requests, validating requests and managing permissions through the Access Control Layer (ACL). - -- **Slim Listener**: Monitors blockchain events and forwards FHE operation requests to the off-chain execution environment. - -- **Result Processor**: Handles FHE operation results from the computation layer and publishes them back to the blockchain. - -- **FHEOS Server**: Executes the actual FHE operations on encrypted data and maintains the encrypted state. - -- **Threshold Network**: A distributed system that securely handles decryption requests through multi-party computation, ensuring no single entity can access the decryption key. - -- **Ciphertext Registry**: Per-host-chain registry that maintains references to encrypted values during a chain's lifecycle. - -- **Commitment Registry**: Single-chain (Arbitrum One) registry where the coprocessor records `(version, handle) → commitHash` entries for every FHE computation result. The Threshold Network reads it to verify ciphertext integrity before issuing a decryption — distinct from the host-chain Ciphertext Registry above. - -### Data Flows - -CoFHE implements several critical data flows that maintain privacy throughout the computation lifecycle: - -1. **Encryption Request**: Manages the secure encryption of input data via ZK proofs before it enters the blockchain. - -2. **FHE Operation Flow**: Handles the process of requesting and executing computations on encrypted data. - -3. **Decryption Request**: Processes requests to decrypt data using the Threshold Network. - -4. **Decrypt/Seal Output**: Enables users to access encrypted results while maintaining privacy. - -This architecture ensures that data remains encrypted throughout its entire lifecycle while still enabling complex computations, providing a foundation for privacy-preserving blockchain applications. - +CoFHE (Coprocessor for Fully Homomorphic Encryption) lets smart contracts compute on encrypted data. Contracts request operations through onchain calls; a set of offchain services executes them and commits to the results. Data stays encrypted until an authorized decryption, and every result can be verified against an onchain commitment. + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +flowchart TB + subgraph Yours["Your code (outside CoFHE)"] + FHEC["Your contract
uses FHE.sol"] + end + + subgraph App["Application"] + SDK["Client SDK
client-side library"] + end + + subgraph Host["Host chain"] + TM["TaskManager + ACL
tasks, permissions"] + PS["PlaintextsStorage
decrypted results"] + end + + subgraph CoFHE["CoFHE"] + TEE["Teecryptor
TEE enclave, key shares"] + ZK["ZK Verifier
TEE enclave, signing key"] + PRT["Partners
key custodians"] + CTS[("Ciphertext store")] + subgraph ENG["FHE Engine"] + direction TB + L["subscribe to task events"] --> X["process FHE computation"] --> P["post commitments to registry"] + end + end + + subgraph Registry["Registry chain"] + CR["CommitmentRegistry
op commitments"] + end + + FHEC --> TM + SDK -- "encrypt" --> ZK + SDK -- "decrypt" --> TEE + SDK -- "publish result" --> TM + TM --> PS + TM --> L + TEE -- "ACL check" --> TM + TEE --> CTS + TEE -- "verify commitment" --> CR + ZK --> CTS + ENG <--> CTS + P --> CR + PRT --> TEE + PRT --> ZK + + style Yours stroke-dasharray: 6 4 + style PRT stroke-dasharray: 6 4 +``` + +## The onchain contracts + +- **FHE.sol**: the Solidity library your contract imports. It exposes arithmetic, comparison, and logical operations on encrypted values, plus access control (`allow`, `allowGlobal`) and decrypt-result verification. +- **[TaskManager](/deep-dive/cofhe-components/task-manager)**: the gateway for all FHE operation requests. It validates requests, emits task events for the offchain services, and manages permissions through the ACL. +- **[ACL](/deep-dive/cofhe-components/acl)**: the access control contract. It records who may use or decrypt each handle; all writes to it go through the TaskManager. +- **[PlaintextsStorage](/deep-dive/cofhe-components/plaintext-storage)**: stores decrypted results after they are published onchain with a verified Teecryptor signature. +- **[CommitmentRegistry](/deep-dive/cofhe-components/commitment-registry)**: lives on a dedicated registry chain and records a hash commitment for every ciphertext the coprocessor produces or verifies. Teecryptor checks it before decrypting anything. + +## The offchain components + +- **[Client SDK](/client-sdk/introduction/overview)** (`@cofhe/sdk`): the TypeScript library applications use to encrypt inputs, manage [permits](/client-sdk/guides/permits), and decrypt outputs. +- **[ZK Verifier](/deep-dive/cofhe-components/zk-verifier)**: a standalone service that verifies encrypted inputs in batches. It checks each zero-knowledge proof, stores the ciphertexts, and signs one approval for the whole batch. +- **[FHE Engine](/deep-dive/cofhe-components/fhe-engine)**: the execution pipeline. It subscribes to TaskManager events, validates and orders operations, runs them on encrypted data, and posts a commitment for every result. +- **Ciphertext store**: the database that holds every ciphertext, fronted by the CT Server. The ZK Verifier writes verified inputs, the FHE Engine reads operands and writes results, and Teecryptor fetches bytes to decrypt. +- **[Teecryptor](/deep-dive/cofhe-components/teecryptor)**: the decryption service. It runs inside a hardware-attested TEE, authorizes every request against the onchain ACL, verifies the ciphertext commitment, and returns signed or sealed results. +- **Partners**: independent custodians of the key material. Each holds a Shamir share and releases it only to an attested enclave. See [Key Management](/deep-dive/cofhe-components/key-management). + +## Data flows + +1. **[Encryption request](/deep-dive/data-flows/encryption-request-flow)**: an input is encrypted client-side and proven valid with a zero-knowledge proof before it enters the chain. +2. **[FHE operation](/deep-dive/data-flows/fhe-operation-request-flow)**: a contract requests a computation; the coprocessor executes it and commits to the result. +3. **[Decryption](/deep-dive/data-flows/decryption-request-flow)**: the SDK asks Teecryptor to decrypt a handle, either as a signed plaintext for onchain publication or sealed to a permit for offchain reads. + +## Future plans + +Two components run inside hardware-attested TEEs today: Teecryptor, which decrypts, and the ZK Verifier, which checks input proofs. Both move to multi-party computation in one planned step. See [Future Plans](/deep-dive/research/future-plans). diff --git a/deep-dive/cofhe-components/plaintext-storage.mdx b/deep-dive/cofhe-components/plaintext-storage.mdx index d1d7176..05e1688 100644 --- a/deep-dive/cofhe-components/plaintext-storage.mdx +++ b/deep-dive/cofhe-components/plaintext-storage.mdx @@ -1,11 +1,14 @@ --- -title: Plaintexts Storage -description: "Internal smart contract that manages storage and retrieval of plaintext values in the host chain with caching mechanisms" +title: PlaintextsStorage +description: "Host-chain contract that stores published decryption results so contracts can read them synchronously" --- | Aspect | Description | |--------|-------------| -| **Type** | Internal Smart Contract | -| **Function** | Storage and management of plaintext values in the host chain | -| **Responsibilities** | • Manages the storage and retrieval of plaintext values in the system
• Provides caching mechanism for plaintext values to improve retrieval performance
• Ensures secure handling of decrypted data within the CoFHE ecosystem | +| **Type** | Internal smart contract on the host chain. | +| **Function** | Stores published decryption results for synchronous reads. | +| **Responsibilities** | • Holds the plaintext result for every published decryption
• Serves reads through the TaskManager's result getters | +Decryption happens offchain, but contracts need the result onchain. When a signed decrypt result is published through `FHE.publishDecryptResult`, the [TaskManager](/deep-dive/cofhe-components/task-manager) verifies the signature and writes the plaintext here. Only the TaskManager can write to this contract. + +Contracts read results back with `FHE.getDecryptResult(handle)`, which reverts if the result is not yet published, or `FHE.getDecryptResultSafe(handle)`, which returns a ready flag instead. See the [Decryption Flow](/deep-dive/data-flows/decryption-request-flow) for the full path. diff --git a/deep-dive/cofhe-components/result-processor.mdx b/deep-dive/cofhe-components/result-processor.mdx deleted file mode 100644 index cc97a29..0000000 --- a/deep-dive/cofhe-components/result-processor.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Result Processor -description: "Off-chain service that handles FHE operation results and publishes them back to the blockchain" ---- - -| Aspect | Description | -|--------|-------------| -| **Type** | Off-chain result handling service | -| **Function** | Receives computation results from fheOS and publishes them to the blockchain | -| **Responsibilities** | • Receives FHE operation results from the fheOS server
• Sends results to the Data Availability layer
• Publishes decryption results back to the Task Manager on the host chain | - -The Result Processor ensures that computation results from the off-chain fheOS server are properly relayed back to the blockchain, completing the FHE operation lifecycle. diff --git a/deep-dive/cofhe-components/slim-listener.mdx b/deep-dive/cofhe-components/slim-listener.mdx deleted file mode 100644 index b355e49..0000000 --- a/deep-dive/cofhe-components/slim-listener.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Slim Listener -description: "Off-chain service that monitors blockchain events and forwards FHE operation requests to the computation layer" ---- - -| Aspect | Description | -|--------|-------------| -| **Type** | Off-chain event monitoring service | -| **Function** | Listens to blockchain events and forwards FHE operation requests to the fheOS server | -| **Responsibilities** | • Monitors events emitted by the Task Manager contract on the destination chain
• Processes incoming requests and forwards them to the fheOS server
• Ensures reliable delivery of operation requests to the computation layer | - -The Slim Listener acts as the bridge between on-chain events and the off-chain computation layer, ensuring that all FHE operation requests are captured and forwarded for processing. diff --git a/deep-dive/cofhe-components/task-manager.mdx b/deep-dive/cofhe-components/task-manager.mdx index 19df020..4cc37d9 100644 --- a/deep-dive/cofhe-components/task-manager.mdx +++ b/deep-dive/cofhe-components/task-manager.mdx @@ -1,37 +1,45 @@ --- title: TaskManager -description: "On-chain entry point for CoFHE integration that initiates FHE operations, generates unique handles, and verifies decrypt result signatures" +description: "Onchain entry point for CoFHE integration that initiates FHE operations, generates unique handles, and verifies decrypt result signatures" --- - | Aspect | Description | |---------|-------------| -| **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.
• 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 | +| **Type** | Contract deployed on the host chain, UUPS-upgradeable behind a proxy. | +| **Function** | Acts as the onchain entry point for CoFHE integration. | +| **Responsibilities** | • Initiates FHE operations requested through the `FHE.sol` library.
• Generates the unique handles that reference future FHE results.
• Emits structured task events for the offchain services.
• Verifies the signatures on encrypted inputs and published decrypt results. | +| **Deployment** | A separate TaskManager contract is deployed on each supported host chain, enabling chain-specific integrations. | -## Decrypt Result Signature Verification +## Two signers -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. +The TaskManager holds two distinct signer addresses, one per trust boundary: -### Key State +| Signer | Verifies | +|--------|----------| +| verifierSigner | Encrypted inputs: the ZK Verifier signs each verified batch, and `batchVerifyInputs` checks that signature before emitting `InputVerified(ctHash, commitment)` per input. The signature binds the consuming contract, so a batch cannot be replayed elsewhere. | +| decryptResultSigner | Decrypt results: [Teecryptor](/deep-dive/cofhe-components/teecryptor) signs each plaintext it returns, and the publish path checks that signature before storing the result. | -| Variable | Description | -|----------|-------------| -| `decryptResultSigner` | Address of the authorized Threshold Network signer. Set to `address(0)` to skip verification (debug mode). | +## Decrypt result signature verification + +The TaskManager supports **permissionless publishing of decrypt results**. Anyone holding a valid signature from the decryption service can publish the result onchain. Today that signature comes from Teecryptor; a decentralized [Threshold Network](/deep-dive/research/future-plans) is the planned successor. + +Verified results are stored in the [PlaintextsStorage](/deep-dive/cofhe-components/plaintext-storage) contract, where any contract can read them back with `getDecryptResult` (reverts with `DecryptionResultNotReady` if pending) or `getDecryptResultSafe` (returns a ready flag). ### Functions +All take `(ctHash, result, signature)`; the batch variants take parallel arrays. + | 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. | +| publishDecryptResult | Verify the signature and store the decrypt result onchain. Emits `DecryptionResult`. | +| publishDecryptResultBatch | Batch publish multiple results in one transaction for gas efficiency. | +| verifyDecryptResult | Verify a signature without publishing (view). Reverts on failure. | +| verifyDecryptResultSafe | Verify a signature without publishing (view). Returns `false` on failure. | +| verifyDecryptResultBatch | Batch verify (view). Reverts on the first failure. | +| verifyDecryptResultBatchSafe | Batch verify (view). Returns a `bool[]` of per-item outcomes. | +| setDecryptResultSigner | Admin-only. Takes an address; sets the authorized signer. | -### Signature Message Format +### Signature message format The signed message is a fixed **76-byte** buffer: @@ -42,4 +50,4 @@ The signed message is a fixed **76-byte** buffer: | `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. +The message is hashed with `keccak256` and verified using OpenZeppelin's `ECDSA.tryRecover`. The `enc_type` and `chain_id` are derived onchain, binding each signature to a specific ciphertext type and chain. diff --git a/deep-dive/cofhe-components/teecryptor.mdx b/deep-dive/cofhe-components/teecryptor.mdx new file mode 100644 index 0000000..28a6813 --- /dev/null +++ b/deep-dive/cofhe-components/teecryptor.mdx @@ -0,0 +1,95 @@ +--- +title: Teecryptor +description: "Offchain decryption service that decrypts CoFHE ciphertexts inside a hardware-attested Trusted Execution Environment (TEE)" +--- + +| Aspect | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Type** | Offchain service running inside a hardware-attested Trusted Execution Environment (TEE). | +| **Function** | Process decryption requests from the [Client SDK](/client-sdk/introduction/overview). | +| **Responsibilities** | • Authorizes each request against the onchain ACL
• Verifies the ciphertext against its onchain commitment
• Decrypts inside the enclave
• Signs (or seals) the result for delivery | + +Teecryptor is the component that decrypts CoFHE ciphertexts. It holds the FHE secret key, but only inside a TEE: an isolated, hardware-encrypted execution environment (Intel TDX) whose exact code image is proven by remote attestation. The trust model is deliberate and worth stating plainly. Instead of splitting the decryption computation across independent parties, Teecryptor relies on hardware attestation to guarantee that only one specific, reviewable program can ever touch the key. A [Threshold Network](/deep-dive/research/future-plans) that decrypts through multi-party computation is the planned next step and will eventually replace Teecryptor. + +## How a decrypt request is processed + +Every `decrypt` and `sealoutput` request carries a ciphertext handle, a host chain id, and optionally a [permit](/client-sdk/guides/permits), sent on the wire as an `acp` (Access Control Permission). A `sealoutput` request requires one, since the ACP carries the sealing key. Teecryptor then runs a fixed pipeline: + + + + +Teecryptor checks the handle against the onchain ACL through the [TaskManager](/deep-dive/cofhe-components/task-manager) on the requested host chain. With a permit, the contract verifies the permit's EIP-712 signature and calls `isAllowedWithPermission`. Without a permit, the handle must have been marked publicly decryptable, checked via `isPubliclyAllowed`. Both paths fail closed: no onchain allowance, no decryption. + + + +Concurrently with authorization, Teecryptor confirms that the [FHE Engine](/deep-dive/cofhe-components/fhe-engine) posted a commitment for this handle in the [CommitmentRegistry](/deep-dive/cofhe-components/commitment-registry). Commitments are write-once, so a verified result is cached permanently. + + + +The ciphertext is fetched from the CT Server exactly as the FHE Engine stored it, and its bytes must hash to the onchain commitment. A missing commitment or mismatched bytes rejects the request. Teecryptor only ever decrypts bytes the coprocessor committed to publicly. + + + +The stored ciphertext is decrypted directly with the FHE secret key, inside the enclave, without re-expanding it first. Decrypting the committed bytes as-is keeps the integrity check meaningful, because the exact bytes that were hashed are the bytes that get decrypted. + + + +For `decrypt` (the `decryptForTx` path), the plaintext is signed with an ECDSA key that never leaves the enclave, producing a result any contract can verify onchain. For `sealoutput` (the `decryptForView` path), the plaintext is instead encrypted to the permit's sealing key using NaCl `crypto_box` (X25519 with XSalsa20-Poly1305). Only the permit holder can read it, and the SDK unseals it locally. + + + + + +A handle whose ciphertext or commitment has not landed yet is not an error. Teecryptor answers with a retryable status and the SDK re-submits automatically, roughly once per second within a multi-minute budget. + + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +sequenceDiagram + participant SDK as Client SDK + participant Teecryptor + participant TaskManager + participant Registry as CommitmentRegistry + participant Store as CT Server + + SDK->>Teecryptor: decrypt / sealoutput (handle, chain id, acp?) + par Authorization + Teecryptor->>TaskManager: isAllowedWithPermission / isPubliclyAllowed + TaskManager-->>Teecryptor: allowed + and Commitment lookup + Teecryptor->>Registry: commitment for handle + Registry-->>Teecryptor: expected hash + end + Teecryptor->>Store: fetch stored ciphertext + Store-->>Teecryptor: ciphertext bytes + Teecryptor->>Teecryptor: verify bytes hash to commitment + Teecryptor->>Teecryptor: decrypt in enclave, sign (or seal) result + Teecryptor-->>SDK: plaintext + signature (or sealed output) +``` + +## Attestation + +Remote attestation is what turns "trust the operator" into "verify the code". Before Teecryptor can obtain any key material, the TEE hardware produces an attestation of the exact code image it is running. Each key-share custodian independently checks that attestation and releases its share only to the approved image digest. The guarantee this yields: the FHE key can only be reconstructed by the exact reviewed code, running on genuine TEE hardware. Nothing else, including the infrastructure operator, can obtain the key or observe the enclave's memory, which the hardware keeps encrypted. + +## Key custody + +The FHE secret key is never stored whole. It is split with Shamir secret sharing into six shares held by independent parties, with any three required for reconstruction (3-of-6). At boot, the attested enclave collects shares from the custodians, reconstructs the key in memory, and zeroizes the transient share material. The reconstructed key exists only inside the enclave for the lifetime of the process. It is never persisted. The result-signing key travels inside the same split secret, so it too can only materialize inside the attested enclave. See [Key Management](/deep-dive/cofhe-components/key-management) for the ceremony that creates and distributes the shares. + +## Result signing + +Every `decrypt` result is signed so the TaskManager can verify it onchain. Teecryptor builds a fixed 76-byte message and signs its keccak256 hash with secp256k1: + +| 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 layout matches what the TaskManager reconstructs in `_computeDecryptResultHash`, so `FHE.publishDecryptResult(ctHash, result, signature)` verifies the signature onchain before storing the plaintext. Request the signature with the `X-Signature-V-Format: evm` header (the SDK does this for you) so the recovery id is 27/28 and directly compatible with Solidity's `ecrecover`. + +Teecryptor's signer address is registered onchain as `decryptResultSigner` in the TaskManager. Only results signed by that address are accepted. + +## Future plans + +Teecryptor concentrates key custody at runtime in a single attested machine. The planned Threshold Network removes that concentration by decrypting through multi-party computation, where no party ever holds the full key. See [Future Plans](/deep-dive/research/future-plans). diff --git a/deep-dive/cofhe-components/threshold-network.mdx b/deep-dive/cofhe-components/threshold-network.mdx deleted file mode 100644 index 6af3220..0000000 --- a/deep-dive/cofhe-components/threshold-network.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Threshold Network -description: "Off-chain distributed network that processes and executes decryption requests using Multi-Party Computation (MPC) protocols" ---- - -| Aspect | Description | -| -------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -| **Type** | Offchain, distributed network. | -| **Function** | Process and execute decryption requests. | -| **Responsibilities** | • Gets ciphertext decryption requests
• Authenticates and validates them
• Runs an MPC protocol to decrypt the ciphertext | - -In any system utilizing encryption, a crucial step is the eventual decryption of data. For example, if we were to build a privacy-preserving ERC20 contract, users would ultimately need to access their encrypted balances. In the case of CoFHE, this decryption process is managed by the Threshold Network. - -## Motivation - -The Threshold Network is a component of a complex cryptographic system with the sole purpose of enhancing the security and trustworthiness of the system by distributing control of the decryption process. Rather than having a single secret key stored and used for the decryption by a centralized entity, we distribute secret shares (to hide the original decryption key) among multiple parties. This enforces collaboration among parties in order to decrypt; the parties perform an MPC (Multi-Party Computation) protocol that results in the decrypted value of a given ciphertext block (single ciphertext can contain a multiple of these so called blocks), ensuring that no information about the full secret key is leaked at any time. - -A practical example of a threshold network in practice is vote counting. Multiple representatives of competing parties gather around to count votes from recent elections. In order to attempt voter fraud all of the participating parties would have to collaborate (which is unlikely). Threshold Network is built on the exact same principle. - -## Concept - -Threshold Network performs decryption operations. The Threshold Network is currently initialized by a Trusted Dealer (in the future, we plan to eliminate the Trusted Dealer). The Dealer initially generates a key. The Trusted Dealer uses the private key within a secret-sharing algorithm to generate secret shares to share among individual members. Each member holds exactly one secret share. To perform a decryption, the secret shares are used to perform partial decryptions through a multiparty computation (MPC) protocol. These partial decryptions are then combined into the final plaintext. The protocol requires cooperation from all participants to perform a decryption, ensuring no single entity can decrypt the ciphertext alone. This distributed control mechanism enhances security by preventing unilateral access to encrypted data. - -## Decryption Process - -The Threshold network includes three main components: - -- **Coordinator** - coordinates communication between the party members to perform the MPC protocol. -- **Party Members** - the individual parties that hold a secret share and execute the MPC protocol. -- **Trusted Dealer** - responsible for initializing the protocol, and for providing random data to the party members, needed to perform the protocol securely. - - -Threshold Network Flow - - -All incoming decryption requests reach the Coordinator (1). - -The coordinator splits the CT into individual Learning With Errors (LWE) CT blocks. These blocks then get broadcast to partymembers (2). During this process data is exchanged back and forth until the decryption of all blocks is complete upon which the coordinator reassembles the plaintext from decrypted LWE CT blocks. The plaintext value then gets sent back to the user. - -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/cofhe-components/zk-verifier.mdx b/deep-dive/cofhe-components/zk-verifier.mdx index c847d16..2d7b883 100644 --- a/deep-dive/cofhe-components/zk-verifier.mdx +++ b/deep-dive/cofhe-components/zk-verifier.mdx @@ -1,56 +1,62 @@ --- title: ZK Verifier -description: "Off-chain service that verifies user inputs using Zero-Knowledge Proofs of Knowledge (ZKPoK) to ensure encrypted data is safe to use in smart contracts" +description: "Offchain service that verifies user inputs using Zero-Knowledge Proofs of Knowledge (ZKPoK) to ensure encrypted data is safe to use in smart contracts" --- -**ZK-Verifier** is an essential component for encrypting and providing data as inputs to confidential smart contracts. +**ZK Verifier** checks every encrypted input before it can enter a confidential smart contract. | Aspect | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Type** | Off-chain service, used by clients. | +| **Type** | Offchain service running inside a hardware-attested TEE. | | **Function** | Verifies the user's input, ensuring that it is safe to use. | -| **Responsibilities** | • Receives a user's ZKPoK of their inputs.
• Verifies said ZK proofs.
• Generates a signature, allowing the user to use these inputs in a smart contract function call.
• Stores inputs and their proofs in GCS public bucket.
• Communicates directly with FheOS. | +| **Responsibilities** | • Receives users' ZKPoKs for their input batches.
• Verifies those proofs.
• Signs one approval per verified batch.
• Stores the verified ciphertext bytes. | -## ZKPoK - Why? +## Why ZKPoK? -**Zero-Knowledge Proof of Knowledge (ZKPoK)** provides a crucial security mechanism in CoFHE. It allows users to prove they know the plaintext of an encrypted input they're sending to a smart contract, without revealing the plaintext itself. +A **Zero-Knowledge Proof of Knowledge (ZKPoK)** lets a user prove they know the plaintext behind an encrypted input, without revealing that plaintext. ZKPoKs protect against potential malicious vectors, including: -1. **Malleability Attacks**: Without ZKPoK protection, attackers could manipulate encrypted data by applying transformations to observed ciphertexts, even without knowing what's inside them. For example, they might combine existing ciphertexts with encrypted zero values to create new valid-looking encrypted data, potentially compromising user's confidentiality. +1. **Malleability attacks**: Without ZKPoK protection, attackers could transform observed ciphertexts into new valid-looking ones without knowing their contents, for example by combining them with encrypted zeros. -2. **Chosen Ciphertext Attacks (CCAs)**: Attackers can submit modified ciphertexts to the system and observe the results, potentially exploiting homomorphic operations to infer sensitive information, manipulate data, or even recover the secret key. +2. **Chosen ciphertext attacks (CCAs)**: Attackers submit modified ciphertexts and observe the results, potentially exploiting homomorphic operations to infer sensitive information or even recover the secret key. -Requiring a ZKPoK for each encrypted input helps running an encryption system in the public space that is a blockchain runtime. It ensures that only users with knowledge of the original plaintext can produce valid proofs. This approach eliminates multiple security risks, protecting sensitive user data and maintaining the system's integrity. +Requiring a ZKPoK for each encrypted input is what makes it safe to run an encryption system in a public runtime like a blockchain. Only someone who knows the original plaintext can produce a valid proof. ## Sending encrypted inputs -As mentioned before, when providing ciphertexts as an input to a smart contract, users have to generate a ZKPoK and get a verification approval first. Although most of the work will be handled by the Client SDK (`@cofhe/sdk`) and FHE.sol, we will describe this mechanism in high-level (also in the diagram below). +When providing ciphertexts as an input to a smart contract, users have to generate a ZKPoK and get a verification approval first. The Client SDK (`@cofhe/sdk`) and `FHE.sol` handle most of this work; the mechanism is described here end to end (also in the diagram below). ZK Proof of Knowledge Flow -The process of sending input(s) to a Smart Contract: +The process of sending inputs to a smart contract: -1. User encrypts an input(s) and generate a ZK proof of knowledge for it. -2. User sends the ciphertext(s) and proof(s) to the ZKVerifier. -3. ZK-Verifier verifies the proof. If valid, sign a message that approves input(s). -4. ZK-Verifier returns the signed approval to User. -5. User sends `(ciphertext, signed_approve)` (one or more) as input(s) to a contract call. -6. Contract verifies the signed message, approving the input(s). -7. Contract performs actual logic. +1. The user encrypts the inputs and generates a ZK proof of knowledge for them. +2. The user sends the ciphertexts and proofs to the ZK Verifier, as one batch. +3. The ZK Verifier verifies each proof. If all are valid, it stores the encrypted values in the ciphertext database and signs one message approving the whole batch. +4. The ZK Verifier returns the handles and the batch approval to the user. +5. The user sends the handles and the approval as inputs to a contract call. +6. The contract verifies the batch signature, approving the inputs and emitting `InputVerified` per input, which anchors a commitment so each input becomes decryptable later. +7. The contract performs the actual logic. -As mentioned, most of this process is abstracted away. In fact, steps 1-6 are all handled behind the scenes, while step 7 (the actual logic) is, of course, up to the user to write. +Most of this process is abstracted away. Steps 1 to 6 all happen behind the SDK and library calls, while step 7 (the actual logic) is up to you to write. -## ZK-Verifier +## Trust model -The ZK-Verifier is a zk-verification program. It has two purposes: +The ZK Verifier runs inside a hardware-attested TEE (Intel TDX). Its signing key is held as Shamir shares by independent partners and released only to the exact attested code image. Neither the operator nor anyone else can sign approvals outside the reviewed program. See [Key Management](/deep-dive/cofhe-components/key-management) for how shares are created and released. After a successful verification, the service stores the ciphertext bytes in the CT Server and archives the inputs and proofs for auditability. -1. Verify the ZKPoK's of ciphertexts that are intended to be inputted to a CoFHE smart contract. -2. Sign a verification message, allowing the said contract to ensure that the inputs are safe and were validated. +The signed message is verified onchain by the TaskManager using `ecrecover`; the verifier's signer address is registered there as `verifierSigner`. -The signed message that the ZK-Verifier outputs will then be verified by the receiving contract using `ecrecover`. That means that the ZKVerifier's public key will be predetermined and well-known. +## Signature format -The ZKVerifier is intended to run in a TEE to reduce trust and ensure the integrity of the inputs and signed verification messages. +For developers integrating without the SDK, the signature covers a batch digest. Each input is hashed, the hashes are concatenated in input order, and the digest of that concatenation is signed: +```text +h_i = keccak256(ct_hash (32 bytes) || utype (1 byte) || security_zone (1 byte) + || sender (20 bytes) || chain_id (32 bytes, big-endian) || consuming_contract (20 bytes)) +digest = keccak256(h_0 || h_1 || ... || h_n) +``` + +The digest binds the sender and the consuming contract, so a verified batch cannot be replayed by another account or into another contract. The `recid` value the service returns (0 or 1) must be adjusted to 27 or 28 for Solidity's `ecrecover`. diff --git a/deep-dive/data-flows/decryption-request-flow.mdx b/deep-dive/data-flows/decryption-request-flow.mdx index f10bf11..45b5613 100644 --- a/deep-dive/data-flows/decryption-request-flow.mdx +++ b/deep-dive/data-flows/decryption-request-flow.mdx @@ -1,70 +1,126 @@ --- -title: Decryption Request Flow +title: Decryption Flow sidebar_position: 3 -description: "Complete flow of a decryption request in the CoFHE ecosystem through smart contracts" +description: "How CoFHE values get decrypted: decryptForTx with onchain publication, decryptForView with sealed output" --- -# Decryption Request Flow +Decryption in CoFHE is SDK-driven and happens offchain, inside [Teecryptor](/deep-dive/cofhe-components/teecryptor). A contract's role is to grant access and, when the result should go onchain, to read it back after publication. -The process of requesting decryption through Smart Contracts starts the same as every other [FHE Operation Request](/deep-dive/data-flows/fhe-operation-request-flow) 📌steps 1-4 - -Here we'll continue from FheOS server handling such request as follows: + +**Contracts cannot request decryption onchain.** The TaskManager rejects decrypt tasks (`DecryptFunctionNotSupported`), and `FHE.decrypt` no longer exists in the FHE library. The coprocessor never pushes plaintexts into your contract. + -## Flow Diagram +There are two SDK entry points, one per destination: -The following diagram illustrates the complete flow of an FHE Decryption request in the CoFHE ecosystem: +- **`decryptForTx`**: returns the plaintext with a Teecryptor signature you can publish onchain. Guide: [Decrypt to Tx](/client-sdk/guides/decrypt-to-tx). +- **`decryptForView`**: returns the plaintext sealed to your [permit](/client-sdk/guides/permits) (an ACP, Access Control Permission, onchain), for UI display and offchain reads. Guide: [Decrypt to View](/client-sdk/guides/decrypt-to-view). - -End-to-end flow of an FHE Decryption request through the CoFHE system components - + +A freshly computed handle may not be decryptable immediately: its ciphertext and commitment land shortly after the transaction. Until then Teecryptor answers with a retryable status and the SDK re-submits automatically. + -*Figure 1: End-to-end flow of an FHE Decryption request through the CoFHE system components* +## The shared pipeline -## Step-by-Step Flow +Both entry points start the same way. - -The decryption request follows the same initial steps as a standard FHE operation request: - -1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ -Refer to [FHE Operation Request Flow](/deep-dive/data-flows/fhe-operation-request-flow) for details on steps 1-4, which include: -- Integration with the Client SDK -- Requesting an FHE Operation -- Task Manager Processing -- Slim Listener Processing + +The contract that owns the encrypted value marks the handle decryptable: `FHE.allow(handle, account)` for a specific account, or `FHE.allowGlobal(handle)` when the value may become public. Without an ACL grant, Teecryptor refuses the request. - -The FheOS server handles decryption requests: + +The application calls one of the two builders: -1. **Create execution thread** on the fheOS server +```typescript +const { ctHash, decryptedValue, signature } = await client + .decryptForTx(ctHash) + .withPermit() + .execute(); +``` -2. **FheOS server calls the threshold network** with: - - The ciphertext to be decrypted - - Transaction hash from the host chain - - Original operation handle - +```typescript +const balance = await client + .decryptForView(ctHash, FheTypes.Uint32) + .withPermit() + .execute(); +``` - -The Threshold Network performs secure decryption: +The request goes to Teecryptor with the handle, the host chain id, and the permit. For publicly decryptable handles, `decryptForTx` can use `.withoutPermit()` instead. + -- Verify the host chain requested the desired decryption -- Retrieve the actual ciphertext hash from private storage -- Validate ciphertext hash integrity -- Perform secure decryption + +Teecryptor authorizes the request against the onchain ACL, fetches the ciphertext exactly as stored, verifies it against the onchain commitment, and decrypts it inside the attested enclave. See the [Teecryptor page](/deep-dive/cofhe-components/teecryptor) for the full pipeline. - -After decryption is complete: 7️⃣ + + +From here the two paths diverge. + +## The transaction path + +For `decryptForTx`, Teecryptor returns the plaintext together with an ECDSA signature over a fixed 76-byte message (result, encryption type, chain id, ciphertext hash). The signing key lives only inside the enclave, and its address is registered onchain as the TaskManager's `decryptResultSigner`. -- 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 +Anyone holding the signature submits it in a transaction: + +```solidity +FHE.publishDecryptResult(ctHash, result, signature); +``` + +The TaskManager recomputes the message hash, recovers the signer, and rejects anything not signed by `decryptResultSigner`. On success it stores the plaintext in PlaintextsStorage and emits a `DecryptionResult` event. `publishDecryptResultBatch` amortizes gas across multiple results. -This enables permissionless result delivery — anyone holding a valid signature can publish. This is useful for client-driven settlement or relayer patterns. +Publication is permissionless: any relayer with a valid signature can deliver the result. `decryptForTx` itself costs no gas; gas is paid only by this publish transaction. - - +Once published, any contract reads the plaintext: + +```solidity +uint64 value = FHE.getDecryptResult(handle); // reverts if not yet published +(uint64 value, bool ready) = FHE.getDecryptResultSafe(handle); // non-reverting variant +``` + +To check a signature without storing the result, use the `verifyDecryptResult` family on the TaskManager. + +## The view path + +For `decryptForView`, Teecryptor never returns a bare plaintext. It encrypts the result to the permit's sealing key, and the SDK unseals it locally, so the plaintext is not exposed in transit. There is nothing to publish; the value goes straight to your application. + +## Flow diagram + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +sequenceDiagram + participant App as Your app + participant SDK as Client SDK + participant Teecryptor + participant Chain as Host chain + + App->>SDK: decryptForTx / decryptForView (ctHash) + SDK->>Teecryptor: decrypt or sealoutput request (handle, chain id, acp?) + Teecryptor->>Chain: ACL check (isAllowedWithPermission / isPubliclyAllowed) + Teecryptor->>Teecryptor: verify commitment, decrypt in enclave + alt decryptForTx + Teecryptor-->>SDK: plaintext + signature + SDK-->>App: { ctHash, decryptedValue, signature } + App->>Chain: FHE.publishDecryptResult(ctHash, result, signature) + else decryptForView + Teecryptor-->>SDK: result sealed to the permit's sealing key + SDK->>SDK: unseal locally + SDK-->>App: plaintext + end +``` + +## Comparison + +| | `decryptForTx` | `decryptForView` | +|---|---|---| +| **Returns** | Plaintext + Teecryptor signature | Plaintext (sealed in transit, unsealed by the SDK) | +| **Use case** | Submit the decrypted value onchain | Display in a UI, offchain reads | +| **Requires permit** | Only if the handle is not publicly decryptable | Yes | +| **Onchain verification** | publishDecryptResult or verifyDecryptResult | Not applicable | +| **Gas cost** | None for the decryption itself; gas only for the publish transaction | None | + +## Future plans + +Decryption is currently performed by Teecryptor inside a hardware-attested TEE. A multi-party Threshold Network is the planned successor; see [Future Plans](/deep-dive/research/future-plans). diff --git a/deep-dive/data-flows/encryption-request-flow.mdx b/deep-dive/data-flows/encryption-request-flow.mdx index 491f29f..a8e78a1 100644 --- a/deep-dive/data-flows/encryption-request-flow.mdx +++ b/deep-dive/data-flows/encryption-request-flow.mdx @@ -1,35 +1,49 @@ --- title: Encryption Request Flow -description: "Complete flow of the encryption request process using the Client SDK for encrypting data for private computation with smart contracts" +description: "How a plaintext becomes a verified encrypted input: local encryption, ZK proof, verification, and onchain use" --- -## Overview +This page follows encrypted inputs from the plaintext in your application to handles a smart contract can compute on. Everything sensitive happens client-side: the values are encrypted and proven locally, and only the ciphertexts and their proofs ever leave the user's machine. Inputs travel as one batch, verified with a single signature that is bound to the contract that will consume them. -This document outlines the complete flow of the encryption request process using the `@cofhe/sdk` Client SDK, a TypeScript library designed to help users encrypt data for private computation with smart contracts. Understanding this process is essential for developers who want to enable their users to interact with privacy-preserving smart contracts using encrypted inputs. - -## Key Components +## Key components | Component | Description | |-----------|-------------| -| **dApp** | The decentralized application that interacts with the user and the contracts | -| **Client SDK** (`@cofhe/sdk`) | TypeScript package designed for seamless interaction with Fhenix's co-processor | -| **Threshold Network** | (When applicable) Handles secure decryption operations | - -## Flow Diagram - -The following diagram illustrates the complete flow of an Encryption request in the CoFHE ecosystem: - - -End-to-end flow of an Encryption request through the CoFHE system components - - -*Figure 1: End-to-end flow of an Encryption request through the CoFHE system components* +| **dApp** | The application that interacts with the user and the contracts | +| **Client SDK** (`@cofhe/sdk`) | TypeScript library that encrypts inputs locally and submits them for verification | +| **ZK Verifier** | Attested offchain service that verifies the proofs of every encrypted-input batch | +| **CT Server** | Stores the verified ciphertext bytes for the coprocessor | +| **[TaskManager](/deep-dive/cofhe-components/task-manager)** | Verifies the ZK Verifier's batch signature when the inputs are used onchain | + +## Flow diagram + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +sequenceDiagram + participant App as Your app + participant SDK as Client SDK + participant ZK as ZK Verifier + participant CTS as CT Server + participant TM as TaskManager + participant CR as CommitmentRegistry + + App->>SDK: encryptInputs([...]).setConsumingContract(addr).execute() + SDK->>SDK: encrypt with TFHE, generate zkPoKs + SDK->>ZK: ciphertext batch + proofs + ZK->>ZK: verify the proofs in the attested TEE + ZK->>CTS: store ciphertext bytes + ZK-->>SDK: handles + one batch signature + SDK-->>App: [handles..., proof] + App->>TM: contract call with handles + proof + TM->>TM: batchVerifyInputs, emit InputVerified per input + TM--)CR: input commitments (relayed by the coprocessor) +``` -## Step-by-Step Flow +## Step-by-step flow -Install, include and initialize the Client SDK in your project (full details [here](/client-sdk/introduction/installation)). +Install and initialize the Client SDK in your project. Full details are in the [installation guide](/client-sdk/introduction/installation). ```bash npm install @cofhe/sdk @@ -41,26 +55,28 @@ const { createCofheConfig, createCofheClient } = require("@cofhe/sdk/node"); ``` - -1️⃣ The data is encrypted locally using the `encrypt` function. + +The application encrypts its values with a single builder call, naming the contract that will consume them: -Under the hood, `encrypt` encrypts the data using the TFHE library and create a zkPoK to prove the encryption is correct. - - - -The zkPoK is verified using the `verify` function. 2️⃣ +```typescript +const [encryptedInput, proof] = await cofheClient + .encryptInputs([Encryptable.uint32(42n)]) + .setConsumingContract(contractAddress) + .execute(); +``` -This verification process ensures that the ciphertext was generated correctly—that it represents a valid encryption of a known plaintext—and that the data has not been tampered with. Upon successful verification, the encrypted data is stored in the Data Availability (DA) layer. 3️⃣ 4️⃣ +Internally, `encryptInputs` encrypts each value with the TFHE library and generates a zkPoK (zero-knowledge proof of knowledge) that the encryption is correct. It then submits the whole batch to the ZK Verifier. The verifier's signature is bound to the consuming contract, so the batch cannot be replayed into a different one. + -The function returns a value handle that can be used to reference the encrypted data later, along with a signature. 5️⃣ + +The ZK Verifier checks each proof inside its attested enclave. A valid proof shows the ciphertext is a correct, untampered encryption of a known plaintext. On success, the verifier stores the ciphertext bytes and returns the handles with one signature covering the batch. The SDK hands your application the tuple `[handles..., proof]`; each handle is an `external` encrypted value such as `externalEuint32`. - -The user can send the value handle to the contract as an encrypted input. This handle represents the ciphertext stored in the DA layer and allows the contract to reference the encrypted value. + +The application passes the handles and the proof to the contract as encrypted inputs. When the contract consumes them, the TaskManager's `batchVerifyInputs` checks the ZK Verifier's signature over the batch and emits an `InputVerified` event per input. The coprocessor picks those events up and posts a commitment for each input to the CommitmentRegistry, so the ciphertexts are anchored onchain like every computed result. -Read more about the implementation details [here](/client-sdk/guides/encrypting-inputs) +Read more about the client-side API in [Encrypting Inputs](/client-sdk/guides/encrypting-inputs). - diff --git a/deep-dive/data-flows/fhe-operation-request-flow.mdx b/deep-dive/data-flows/fhe-operation-request-flow.mdx index 434716a..bde5255 100644 --- a/deep-dive/data-flows/fhe-operation-request-flow.mdx +++ b/deep-dive/data-flows/fhe-operation-request-flow.mdx @@ -1,111 +1,99 @@ --- title: FHE Operation Request Flow sidebar_position: 2 -description: "Complete flow of an FHE operation request in the CoFHE ecosystem through smart contracts" +description: "How an FHE operation travels from a smart contract call through the coprocessor to an onchain commitment" --- -# FHE Operation Request Flow +This page follows a single FHE operation from the contract call that requests it to the commitment that anchors its result onchain. The request itself is synchronous: the contract gets a handle for the result immediately, while the coprocessor computes the actual ciphertext in the background. -## Overview - -This document outlines the complete flow of an FHE (Fully Homomorphic Encryption) operation request in the CoFHE ecosystem through Smart Contracts. Understanding this process is essential for developers integrating private computation capabilities into their smart contracts. - -## Key Components +## Key components | Component | Description | |-----------|-------------| -| **dApp** | The decentralized application that requests FHE operations | -| **FHE.sol** | The library providing FHE operation functions | -| **Task Manager** | Verifies and forwards operation requests | -| **Slim Listener** | Monitors blockchain events and forwards requests to the execution layer | -| **Result Processor** | Handles operation results and publishes them back to the blockchain | -| **fheOS Server** | Executes the actual FHE operations | -| **Threshold Network** | (When applicable) Handles secure decryption operations | - -## Flow Diagram - -The following diagram illustrates the complete flow of an FHE operation request in the CoFHE ecosystem: - - -End-to-end flow of an FHE operation request through the CoFHE system components - - -*Figure 1: End-to-end flow of an FHE operation request through the CoFHE system components* +| **Your contract** | Requests FHE operations through the FHE.sol library | +| **FHE.sol** | The Solidity library providing FHE operation functions | +| **[TaskManager](/deep-dive/cofhe-components/task-manager)** | Validates requests, checks the [ACL](/deep-dive/cofhe-components/acl), and emits task events | +| **[FHE Engine](/deep-dive/cofhe-components/fhe-engine)** | Picks up task events, validates and orders the work, executes it, and commits the result | +| **[CommitmentRegistry](/deep-dive/cofhe-components/commitment-registry)** | Records a hash commitment for every result ciphertext | + +## Flow diagram + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% +sequenceDiagram + participant Contract as Your contract (FHE.sol) + participant TM as TaskManager + participant Engine as FHE Engine + participant CR as CommitmentRegistry + + Contract->>TM: FHE.add(lhs, rhs) calls createTask + TM->>TM: validate inputs, check ACL + TM-->>Contract: result handle (synchronous) + TM->>Engine: TaskCreated event + Engine->>Engine: validate and order the task + Engine->>Engine: execute op, store ciphertext under the handle + Engine->>CR: postCommitments (batched) +``` -## Step-by-Step Flow +## Step-by-step flow - -The decentralized application (dApp) integrates with CoFHE by utilizing the **Client SDK** (`@cofhe/sdk`) for encryption. - -[See in GitHub](https://github.com/FhenixProtocol/cofhesdk) 1️⃣ - -[Encrypt request](/deep-dive/data-flows/encryption-request-flow) using the Client SDK, returns `InEuint` structure. + +The application encrypts its input client-side and proves it valid, producing an encrypted-input handle plus a batch proof the contract can accept. The [Encryption Request Flow](/deep-dive/data-flows/encryption-request-flow) covers this step. -This step happens on the client side before blockchain interaction. +This step happens on the client side, before any blockchain interaction. - -When the dApp needs to perform an encrypted operation within the smart contract: 2️⃣ - -**Import the FHE library in Solidity:** + +Import the FHE library in Solidity: ```solidity import "@fhenixprotocol/cofhe-contracts/FHE.sol"; ``` -**Call the appropriate FHE function** from the imported library: +Call the appropriate FHE function from the imported library: ```solidity -// using trivial encrypt or the returned structures from the previous step. -function addExample(InEuint32 encryptedInput) { - euint32 lhs = FHE.asEuint32(encryptedInput); +// Using trivial encrypt or the handle and proof from the previous step. +function addExample(externalEuint32 input, bytes calldata proof) public { + euint32 lhs = FHE.asEuint32(input, proof); euint32 rhs = FHE.asEuint32(10); - + // Request an operation (addition in this example) euint32 result = FHE.add(lhs, rhs); } ``` -**FHE.sol forwards the request** to the Task Manager contract +FHE.sol forwards the request to the TaskManager contract. - -The Task Manager serves as the gateway for all FHE operation requests: + +The TaskManager is the gateway for all FHE operation requests. It: -1. **Validate request structure** to ensure all inputs are properly formatted -2. **Verify access permissions** by checking if the caller has proper access to the encrypted inputs (using ACL.sol) 3️⃣ -3. **Generate a unique handle** that will be used to reference the future ciphertext result -4. **Return the handle** to the calling dApp contract -5. **Emit an event** containing the operation details for the Slim Listener to process 4️⃣ +1. Validates the request structure, so all inputs are properly formatted. +2. Verifies access permissions: the caller must have ACL access to every encrypted input. +3. Generates a unique handle that will reference the future ciphertext result. +4. Returns the handle to the calling contract, synchronously. Subsequent operations can chain on it right away. +5. Emits a `TaskCreated` event with the operation details for the offchain services. - -The Slim Listener monitors and forwards FHE operation requests: - -1. **Listen for events** from the Task Manager 5️⃣ -2. **Forward request details** to the fheOS server 6️⃣ + +The [FHE Engine](/deep-dive/cofhe-components/fhe-engine) subscribes to TaskManager events on every host chain and picks up the `TaskCreated` event. It checks that the operation is well formed and that the inputs it references exist. An operation that arrives before its inputs finish computing is deferred and released once they land. - -The FheOS server handles requests: + +The engine then: -1. **Create execution thread** on the fheOS server -2. **Execute the requested operation** on encrypted data -3. **Generate result ciphertext** containing the encrypted result -4. **Map the handle** to the actual ciphertext hash in the private storage -5. **Make result available** for subsequent operations -6. **Notify the Result Processor** of operation completion +1. Executes the requested operation on the encrypted data. +2. Stores the result ciphertext in the ciphertext database, keyed by the handle. +3. Releases any dependent operations that were deferred while waiting for this result. - -For standard FHE operations (not decryption): - -1. **Update ciphertext registry** with the new encrypted result 7️⃣ + +The engine produces a commitment for the result, the hash of the stored ciphertext bytes, and posts it in a batch to the CommitmentRegistry on the registry chain. The commitment anchors the result. Teecryptor will only decrypt ciphertext bytes that hash to a registered commitment. -At this point **the operation cycle is completed**, preserving the confidentiality of all encrypted values. +At this point the operation cycle is complete, and the confidentiality of every encrypted value is preserved. - diff --git a/deep-dive/data-flows/off-chain-decryption-flow.mdx b/deep-dive/data-flows/off-chain-decryption-flow.mdx deleted file mode 100644 index e5c35fe..0000000 --- a/deep-dive/data-flows/off-chain-decryption-flow.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Off-Chain Decryption Flow -sidebar_position: 4 -description: "Complete flow of off-chain decryption using decryptForTx and decryptForView" ---- - -# Off-Chain Decryption Flow - -## Overview - -This document lays out the complete flow of off-chain decryption requests. There are two methods for decrypting encrypted data off-chain: - -- **`decryptForTx`** — Returns the plaintext value and a Threshold Network signature. Used when the decrypted value needs to be submitted on-chain (e.g., via `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`). -- **`decryptForView`** — Returns only the plaintext value. Used for UI display or off-chain reads where no on-chain proof is needed. - -## Key Components - -| Component | Description | -| --------------------- | ------------------------------------------------------------------------------------ | -| **CtHash** | A `bytes32` handle representing an encrypted value. Fetched on-chain. | -| **SDK Client** | Client library handling `permits` and the `decryptForTx` / `decryptForView` operations. | -| **Threshold Network** | Decentralized decryption network that handles the requests and produces signatures. | -| **ACL** | On-chain **A**ccess **C**ontrol **L**ist responsible for tracking **CtHash** access. | - -## decryptForTx Flow - -Use `decryptForTx` when you need to submit the decrypted value on-chain with a proof. - - - -Solidity contract: - -```solidity -contract Example { - euint32 public count; - - function setCount(uint32 num) public { - count = FHE.asEuint32(num); - FHE.allowThis(count); - FHE.allowPublic(count); // Allow anyone to request decryption - } -} -``` - -Fetch the `CtHash` from the chain: - -```typescript -const ctHash = await example.count(); -``` - - -All encrypted types (`euint8`, `euint16`, `euint32`, `euint64`, `euint128`, `ebool`, `eaddress`) are wrappers around `bytes32`. The data returned from the contract can be used as a `CtHash` directly. - - - - -Call `decryptForTx` on the SDK client. Since `FHE.allowPublic` was used, no permit is needed: - -```typescript -const result = await client - .decryptForTx(ctHash) - .withoutPermit() - .execute(); -``` - -If the value was granted access via `FHE.allow` (not `allowPublic`), use `.withPermit()` instead: - -```typescript -const result = await client - .decryptForTx(ctHash) - .withPermit() - .execute(); -``` - - - -Behind the scenes: - -1. The SDK sends the decryption request to the Threshold Network -2. The Threshold Network verifies on-chain that the requester has access to the `CtHash` via the ACL -3. The Threshold Network performs secure decryption -4. The Threshold Network signs the plaintext result and returns both the plaintext and the signature - - - -The SDK returns an object containing the decrypted value and signature. Submit these on-chain: - -```typescript -// result contains: { ctHash, decryptedValue, signature } - -await example.revealCount( - result.decryptedValue, - result.signature -); -``` - -The on-chain function verifies the signature using `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`: - -```solidity -function revealCount(uint32 _decrypted, bytes memory _signature) external { - FHE.publishDecryptResult(count, _decrypted, _signature); -} -``` - - - ---- - -## decryptForView Flow - -Use `decryptForView` when you only need to display the value in the UI — no on-chain transaction is needed. - - - -The contract must have granted access to the user via `FHE.allow` or `FHE.allowSender`: - -```solidity -contract Example { - mapping(address => euint32) private balances; - - function getBalance() public view returns (euint32) { - return balances[msg.sender]; - } - - function deposit(uint32 amount) public { - balances[msg.sender] = FHE.asEuint32(amount); - FHE.allowThis(balances[msg.sender]); - FHE.allowSender(balances[msg.sender]); // Grant access to the user - } -} -``` - -```typescript -const ctHash = await example.getBalance(); -``` - - - -Call `decryptForView` with a permit (required since this is user-specific data): - -```typescript -const result = await client - .decryptForView(ctHash) - .withPermit() - .execute(); - -console.log(`Balance: ${result.decryptedValue}`); -``` - - - -Behind the scenes: - -1. The SDK sends the decryption request with the user's permit to the Threshold Network -2. The Threshold Network verifies the permit's signature and checks on-chain that `permit.issuer` has access to the `CtHash` via the ACL -3. The Threshold Network performs secure decryption -4. The plaintext value is returned to the SDK (no signature needed since this is view-only) - - - ---- - -## Comparison - -| | `decryptForTx` | `decryptForView` | -|---|---|---| -| **Returns** | Plaintext + Threshold Network signature | Plaintext only | -| **Use case** | Submit decrypted value on-chain | Display in UI | -| **Requires permit** | Only if not `allowPublic` | Yes | -| **On-chain verification** | `publishDecryptResult` or `verifyDecryptResult` | Not applicable | -| **Gas cost** | Yes (on-chain tx needed) | None | diff --git a/deep-dive/research/future-plans.mdx b/deep-dive/research/future-plans.mdx index af70af2..af3cf4a 100644 --- a/deep-dive/research/future-plans.mdx +++ b/deep-dive/research/future-plans.mdx @@ -3,44 +3,48 @@ title: Future Plans sidebar_position: 9 description: "Roadmap for CoFHE decentralization, upcoming features, and planned improvements" --- -Future Plans - -## Road to Decentralization - -Integrating FHE into a blockchain-runtime is a hard and complex task. Our engineering philosophy is _Ship Fast_, and we believe that to build the best possible product we need to meet real users early. Similar to the approach described in [Vitalik's "training wheels" post](https://ethereum-magicians.org/t/proposed-milestones-for-rollups-taking-off-training-wheels/11571) (in the context of rollups), we too are relying on "training wheels" releasing CoFHE to achieve this goal. - -Outlined here is a non-exhaustive list of trust-points, centralized components and compromises made to ship CoFHE to users as fast as possible, along with how we plan to address them in the future. This list will be updated as things progress. - -| Component | Compromise | Plan to solve | Timeline | Status | -| ---------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------- | -------- | ------ | -| Threshold Network (TN) | All parties are run by Fhenix | N/A | N/A | ❌ | -| Threshold Network (TN) | Use of a Trusted Dealer for keys and random data generation | N/A | N/A | ❌ | -| Threshold Network (TN) | Parties trust the Coordinator | N/A | N/A | ❌ | -| Threshold Network (TN) | TN trusts CoFHE (tx-flow decryptions) | N/A | N/A | ❌ | -| Threshold Network (TN) | Parties trust a Trusted Dealer | 1. Run TD in a TEE
2. Public ceremony for share creation
3. Eliminate TD | N/A | ❌ | -| Threshold Network (TN) | Parties are not using unique random data within the protocol | Pull random data from the TD | N/A | ❌ | -| Threshold Network (TN) | SealOutput reencryption performed in a centralized manner | N/A | N/A | ❌ | -| ZK-Verifier (ZKV) | CoFHE trusts ZK-Verifier | Run ZKV in a TEE | N/A | ❌ | -| CoFHE | Trust in CoFHE to perform correct FHE computations | External verification using AVS | N/A | ❌ | -| CoFHE | User inputs stored in a centralized manner | Use a decentralized DA | N/A | ❌ | -| All | Codebase is unaudited | Perform a security audit | N/A | ❌ | -| All | Codebase is not fully open-source | Open-source codebase | N/A | ❌ | - -## Upcoming Features - -In the spirit of transparency, here we describe the general feature-roadmap planned for CoFHE. This list will be updated as things progress. - -| Feature | Type | Description | Timeline | Status | -| ------------------------------ | ------------------- | -------------------------------------------------------------------------------- | -------- | ------ | -| Integration SDK | DevX | SDK to easily integrate CoFHE-specific components into dApps | N/A | ❌ | -| Additional external devtools | DevX | Remix, Alchemy SDK and more | N/A | ❌ | -| RNG | DevX | Ability to generate secure randomness in contracts | N/A | ❌ | -| Alternative runtimes | DevX | Support for additional runtimes other than EVM | N/A | ❌ | -| FHE ops in view functions | DevX | Ability to execute FHE operations in view functions in contracts | N/A | ❌ | -| GPU support | UX | Run FHE operations on a GPU backend, improving performance and overall latency | N/A | ❌ | -| FPGA support | UX | Run FHE operations on an FPGA backend, improving performance and overall latency | N/A | ❌ | -| T-out-of-N MPC protocol | Robustness | Improve robustness of the TN by not requiring all parties to be online | N/A | ❌ | -| Support additional host-chains | DevX/UX | N/A | N/A | ❌ | -| Key shares rotation | Robustness/Security | Ability to rotate the party shares in the TN | N/A | ❌ | -| Key Rotation | Robustness/Security | Ability to rotate the key for the entire protocol | N/A | ❌ | +## Road to decentralization + +Integrating FHE into a blockchain runtime is a hard and complex task. Our engineering philosophy is to ship fast: to build the best possible product we need to meet real users early. Similar to the approach described in [Vitalik's "training wheels" post](https://ethereum-magicians.org/t/proposed-milestones-for-rollups-taking-off-training-wheels/11571) (in the context of rollups), CoFHE ships with training wheels of its own. + +Outlined here is a non-exhaustive list of trust points, centralized components, and compromises made to ship CoFHE to users as fast as possible. Each row notes how we plan to address it, and the list will be updated as things progress. + +| Component | Compromise | Plan to solve | Status | +| --- | --- | --- | --- | +| Teecryptor | Decryption is served by a single TEE service operated by Fhenix | Replace with multi-party computation (below) | Planned | +| Teecryptor | Trust in the TEE hardware vendor and its attestation chain | Multi-party computation removes the hardware trust anchor | Planned | +| ZK Verifier | Input proofs are checked by a single TEE service operated by Fhenix | Move to multi-party computation, with each partner verifying inputs on its own side | Planned | +| Key custody | The FHE key is reconstructed inside one attested enclave at runtime | Threshold decryption, where no party ever holds the full key | Planned | +| CoFHE | Trust in CoFHE to perform correct FHE computations | External verification | Planned | +| CoFHE | User inputs stored in a centralized manner | Use a decentralized DA | Planned | +| All | Codebase is unaudited | Perform a security audit | Planned | +| All | Codebase is not fully open source | Open-source the codebase | Planned | + +One training wheel has already been narrowed: the ZK Verifier now runs inside a hardware-attested TEE, with its signing key released only to the attested code image. The enclave itself stays a trust point until the move described below. + +## The path to multi-party computation + +CoFHE runs two TEE services today, [Teecryptor](/deep-dive/cofhe-components/teecryptor) and the [ZK Verifier](/deep-dive/cofhe-components/zk-verifier). Both move to multi-party computation, and they move together. No TEE stays on the critical path after that. + +Decryption becomes a Threshold Network. Independent parties decrypt through multi-party computation, so no single party (and no single machine) ever holds the FHE key. Robustness goals for it include a t-out-of-n protocol (no requirement that every party stays online), party share rotation, and full protocol key rotation. + +Input verification follows the same model. Each partner runs ZK verification on its own side, instead of one attested enclave checking every proof and signing the batch alone. + +See [Research in Fhenix](/deep-dive/research/research-in-fhenix) for the threshold decryption protocol behind this plan. + +## Upcoming features + +In the spirit of transparency, here is the general feature roadmap planned for CoFHE. + +| Feature | Type | Description | Status | +| --- | --- | --- | --- | +| RNG | DevX | Secure randomness in contracts (`FHE.random`) | Shipped | +| Additional host chains | DevX/UX | Multiple EVM host chains (three run on the current testnet) | Shipped, more planned | +| Hardhat and Foundry plugins | DevX | First-party tooling for the standard EVM dev stacks | Shipped | +| React SDK | DevX | `@cofhe/react` hooks and components for dApp frontends | Shipped | +| Additional external devtools | DevX | Remix, Alchemy SDK and more | Planned | +| GPU support | UX | Run FHE operations on a GPU backend for lower latency | In progress | +| FPGA support | UX | Run FHE operations on an FPGA backend | Planned | +| FHE ops in view functions | DevX | Execute FHE operations in contract view functions | Planned | +| Alternative runtimes | DevX | Support for runtimes other than the EVM | Planned | diff --git a/deep-dive/research/research-in-fhenix.mdx b/deep-dive/research/research-in-fhenix.mdx index c92bb86..3978c31 100644 --- a/deep-dive/research/research-in-fhenix.mdx +++ b/deep-dive/research/research-in-fhenix.mdx @@ -4,11 +4,10 @@ sidebar_position: 10 description: "Research into novel cryptographic techniques and optimization of Fully Homomorphic Encryption for blockchain applications" --- -# Research in Fhenix +Our research explores novel cryptographic techniques to push the boundaries of Fully Homomorphic Encryption (FHE). We mainly work on optimizing the latency of FHE-based smart contracts using the latest published schemes, keeping them both efficient and secure. Ultimately, the aim is to broaden the practical viability of FHE by delivering protocols that meet real-world performance needs. -Our research explores novel cryptographic techniques to push the boundaries of Fully Homomorphic Encryption (FHE). We mainly delve into optimizing the latency of FHE-based smart contracts using state-of-the-art schemes, ensuring they remain both efficient and secure. Ultimately, our aim is to broaden the practical viability of FHE by delivering protocols that meet real-world performance needs. We also designed a secure high performance threshold decryption protocol (see below). +## Threshold decryption for FHE -## Current Project: Threshold Decryption for FHE - -We designed a new threshold FHE decryption protocol that achieves both **unprecedented throughput** and **shortest latency** compared to existing solutions. Specifically, it improves **throughput by ~20,000×** and **cuts latency by up to 37×** relative to the state of the art. This is achieved by securely removing ciphertext noise with an efficient MPC-based approach, eliminating the need for noise flooding while maintaining strong simulation-based security. +Decryption in CoFHE is currently served by [Teecryptor](/deep-dive/cofhe-components/teecryptor), a TEE-based service. The research below is the path to its planned decentralized successor, the [Threshold Network](/deep-dive/research/future-plans). +We designed a new threshold FHE decryption protocol that improves throughput by roughly 20,000x and cuts latency by up to 37x relative to the prior state of the art. This is achieved by securely removing ciphertext noise with an efficient MPC-based approach, eliminating the need for noise flooding while maintaining strong simulation-based security. diff --git a/docs.json b/docs.json index 2d604ef..1e50305 100644 --- a/docs.json +++ b/docs.json @@ -238,14 +238,12 @@ "deep-dive/cofhe-components/overview", "deep-dive/cofhe-components/task-manager", "deep-dive/cofhe-components/acl", - "deep-dive/cofhe-components/slim-listener", - "deep-dive/cofhe-components/result-processor", "deep-dive/cofhe-components/plaintext-storage", - "deep-dive/cofhe-components/ct-registry", "deep-dive/cofhe-components/commitment-registry", "deep-dive/cofhe-components/zk-verifier", - "deep-dive/cofhe-components/fheos-server", - "deep-dive/cofhe-components/threshold-network" + "deep-dive/cofhe-components/fhe-engine", + "deep-dive/cofhe-components/teecryptor", + "deep-dive/cofhe-components/key-management" ] }, { @@ -253,8 +251,7 @@ "pages": [ "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/off-chain-decryption-flow" + "deep-dive/data-flows/decryption-request-flow" ] }, { @@ -309,6 +306,30 @@ { "source": "/client-sdk/guides/permits", "destination": "/client-sdk/guides/acps" + }, + { + "source": "/deep-dive/cofhe-components/threshold-network", + "destination": "/deep-dive/cofhe-components/teecryptor" + }, + { + "source": "/deep-dive/cofhe-components/slim-listener", + "destination": "/deep-dive/cofhe-components/fhe-engine" + }, + { + "source": "/deep-dive/cofhe-components/fheos-server", + "destination": "/deep-dive/cofhe-components/fhe-engine" + }, + { + "source": "/deep-dive/cofhe-components/result-processor", + "destination": "/deep-dive/cofhe-components/fhe-engine" + }, + { + "source": "/deep-dive/cofhe-components/ct-registry", + "destination": "/deep-dive/cofhe-components/commitment-registry" + }, + { + "source": "/deep-dive/data-flows/off-chain-decryption-flow", + "destination": "/deep-dive/data-flows/decryption-request-flow" } ], "integrations": { diff --git a/get-started/introduction/what-is-cofhe.mdx b/get-started/introduction/what-is-cofhe.mdx index f5ccadb..7decd9d 100644 --- a/get-started/introduction/what-is-cofhe.mdx +++ b/get-started/introduction/what-is-cofhe.mdx @@ -48,6 +48,7 @@ When an authorized user wants a result, they present a signed [Access Control Pe ```mermaid +%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%% sequenceDiagram participant User participant SDK as "@cofhe/sdk" @@ -89,11 +90,8 @@ Developers only interact directly with **two** parts of CoFHE; the rest runs beh | Component | Role | | --- | --- | | **Task Manager** | Onchain gateway that validates FHE requests and enforces access control | -| **Slim Listener** | Watches onchain events and forwards operations to the offchain layer | -| **FheOS Server** | Verifies incoming work and queues it. It does not execute FHE operations | -| **FHE Engine** | Executes the FHE operations on encrypted data | -| **Result Processor** | Publishes verified results back onchain | -| **Threshold Network** | Decrypts via multi-party computation, no single party holds the key | +| **FHE Engine** | Picks up onchain task events, validates and orders them, executes the FHE operations on encrypted data, and commits results | +| **Teecryptor** | Decrypts inside a hardware-attested TEE, after checking permissions and commitments | | **Registries** | Track ciphertexts and record result commitments so integrity can be verified before any decryption | For a component-by-component breakdown, see the [CoFHE Architecture deep dive](/deep-dive/cofhe-components/overview). diff --git a/scripts/mermaid-theme.json b/scripts/mermaid-theme.json new file mode 100644 index 0000000..95fe342 --- /dev/null +++ b/scripts/mermaid-theme.json @@ -0,0 +1,41 @@ +{ + "theme": "base", + "themeVariables": { + "fontFamily": "Menlo, Monaco, Consolas, monospace", + "fontSize": "16px", + "primaryColor": "#8FBAF5", + "primaryBorderColor": "#2E7CF6", + "primaryTextColor": "#0A1626", + "lineColor": "#4C8DFF", + "signalColor": "#4C8DFF", + "signalTextColor": "#8FA3BF", + "actorBkg": "#8FBAF5", + "actorBorder": "#2E7CF6", + "actorTextColor": "#0A1626", + "actorLineColor": "#3D4654", + "noteBkgColor": "#14171C", + "noteBorderColor": "#3D4654", + "noteTextColor": "#AFC3DE", + "activationBkgColor": "#1E3A5F", + "activationBorderColor": "#4C8DFF", + "clusterBkg": "#14171C", + "clusterBorder": "#3D4654", + "titleColor": "#E7EAEE", + "edgeLabelBackground": "#8FBAF5", + "textColor": "#AFC3DE", + "labelTextColor": "#E7EAEE", + "tertiaryColor": "#14171C", + "loopTextColor": "#AFC3DE", + "labelBoxBkgColor": "#1E3A5F", + "labelBoxBorderColor": "#4C8DFF" + }, + "sequence": { + "actorFontFamily": "Menlo, Monaco, Consolas, monospace", + "messageFontFamily": "Menlo, Monaco, Consolas, monospace", + "noteFontFamily": "Menlo, Monaco, Consolas, monospace", + "width": 220, + "actorFontSize": 16, + "messageFontSize": 16, + "noteFontSize": 15 + } +} \ No newline at end of file diff --git a/scripts/sync-mermaid-theme.py b/scripts/sync-mermaid-theme.py new file mode 100644 index 0000000..f253664 --- /dev/null +++ b/scripts/sync-mermaid-theme.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Stamp the shared Mermaid init directive into every mermaid block. + +The house theme lives in scripts/mermaid-theme.json. This script inserts the +matching %%{init: ...}%% directive as the first line of every ```mermaid block +in the docs, replacing any existing init line, so all diagrams share one style +and the theme can be changed in one file. + +Usage: + python3 scripts/sync-mermaid-theme.py # rewrite files in place + python3 scripts/sync-mermaid-theme.py --check # exit 1 if any block is out of date +""" + +import json +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +THEME_FILE = ROOT / "scripts" / "mermaid-theme.json" +SKIP_DIRS = {"local", "node_modules", ".git", "snippets"} + +BLOCK_RE = re.compile(r"(```mermaid[ \t]*\n)(.*?)(```)", re.DOTALL) +INIT_RE = re.compile(r"^%%\{init:.*\}%%\s*\n", re.DOTALL) + + +def directive() -> str: + theme = json.loads(THEME_FILE.read_text()) + return "%%{init: " + json.dumps(theme, separators=(", ", ": ")) + "}%%\n" + + +def sync_text(text: str, init_line: str) -> str: + def repl(m: re.Match) -> str: + body = INIT_RE.sub("", m.group(2), count=1) + return m.group(1) + init_line + body + m.group(3) + + return BLOCK_RE.sub(repl, text) + + +def main() -> int: + check = "--check" in sys.argv + init_line = directive() + stale = [] + for path in sorted(ROOT.rglob("*.mdx")): + if any(part in SKIP_DIRS for part in path.parts): + continue + original = path.read_text() + updated = sync_text(original, init_line) + if updated != original: + if check: + stale.append(path.relative_to(ROOT)) + else: + path.write_text(updated) + print(f"updated {path.relative_to(ROOT)}") + if check and stale: + for p in stale: + print(f"stale mermaid theme: {p}") + print("Run: python3 scripts/sync-mermaid-theme.py") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main())