Skip to main content

Documentation Index

Fetch the complete documentation index at: https://cofhe-docs.fhenix.zone/llms.txt

Use this file to discover all available pages before exploring further.

AspectDescription
TypeUUPS-upgradeable Solidity contract deployed on a registry chain (Arbitrum One in production). Distinct from the per-host-chain CTRegistry.
FunctionRecords (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.
DeploymentOne deployment per registry chain, behind an ERC-1967 proxy. Initialized with (initialOwner, initialPoster). Owner is Ownable2Step — transfers require explicit accept.

Why a separate registry chain?

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. This is why the host-chain CTRegistry (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.

Storage shape

mapping(bytes32 version => mapping(bytes32 handle => bytes32 commitHash)) commitments;
mapping(bytes32 version => bytes32[])                                     handlesByVersion;
mapping(bytes32 version => VersionStatus)                                 versionStatus;
mapping(address => bool)                                                  posters;
commitments is the source-of-truth lookup. handlesByVersion is an array kept in parallel so paginated enumeration is O(limit) instead of O(total). Storage lives at the ERC-7201 slot derived from cofhe.storage.CommitmentRegistry, so the contract is upgrade-safe.

Version lifecycle

version is an opaque bytes32 tag chosen by the coprocessor when FHE parameters change (see the FHE Engine COMMITMENT_VERSION notes). Every version moves through a small state machine:
Unset ─┐

     Active ─┬──────► Deprecated ──► Revoked
             └─────────────────────► Revoked
StateMeaningAllowed transitions
UnsetDefault. No commitments have been posted under this version.Active
ActivePosters may write commitments under this version. The Threshold Network honors lookups.Deprecated, → Revoked
DeprecatedNew commitments rejected. Existing lookups still resolve. Used during a parameter rollover.Revoked
RevokedHard kill. No further transitions; the version is dead.— (terminal)
Owner-only setVersionStatus(version, newStatus) enforces these transitions and reverts with InvalidVersionTransition otherwise. The transition emits VersionStatusChanged(version, oldStatus, newStatus).

Roles and write surface

RoleHow it’s setWhat it can do
Ownerinitialize(initialOwner, …), then Ownable2Step transfer.addPoster, removePoster, setVersionStatus, _authorizeUpgrade.
PosterOwner-only addPoster(address). Initial poster supplied to initialize.postCommitments, postCommitmentsSafe.
Non-poster posts revert with OnlyPosterAllowed(caller). In production, the blockchain-poster service holds the only poster role and signs through OpenZeppelin Relayer.

Writing commitments

function postCommitments(
    bytes32 version,
    bytes32[] calldata handles,
    bytes32[] calldata commitHashes
) external onlyPoster;

function postCommitmentsSafe(
    bytes32 version,
    bytes32[] calldata handles,
    bytes32[] calldata commitHashes
) external onlyPoster;
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).
The difference is in how duplicates are handled:
FunctionDuplicate handle under same versionUse case
postCommitmentsReverts the whole batch with CommitmentAlreadyExists(version, handle).Strict integrity — caller knows it’s posting unique data.
postCommitmentsSafeSilently 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. 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

FunctionReturnsNotes
getCommitment(version, handle)bytes32bytes32(0) means “not posted”.
getVersionStatus(version)VersionStatusUnset if never registered.
getSize(version)uint256Number of handles ever committed under version.
getHandleByIndex(version, index)bytes32Direct 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)boolUseful for off-chain 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.

Events

EventEmitted byUse
CommitmentsPosted(bytes32 indexed version, uint256 batchSize)postCommitmentsConfirm a strict batch landed.
CommitmentsPostedSafe(bytes32 indexed version, uint256 newlyPosted, uint256 skipped)postCommitmentsSafeReconcile “how many were new” in an idempotent flow.
VersionStatusChanged(bytes32 indexed version, VersionStatus oldStatus, VersionStatus newStatus)setVersionStatusWatch for Active → Deprecated to know when to stop posting under a version.
PosterAdded(address indexed poster) / PosterRemoved(address indexed poster)addPoster / removePosterAudit 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).

Source