Skip to main content

What a commitment is

A commitment is the keccak256 hash of a ciphertext’s canonical stored bytes. The compute pipeline produces one for every result it computes, and anchors one for every encrypted input the ZK Verifier approves. It batches them and writes them to this registry.

Who writes and who reads

One component writes commitments, and one component reads them. The write happens when a ciphertext is produced; the read happens later, whenever someone asks to decrypt it.
Sequence diagram of the commitment write and read
The compute pipeline and Teecryptor run offchain inside CoFHE; the registry itself is onchain. Hover any component or message in the explorable version.

Why commitments?

The commitment is a safety check in the decryption flow. Before decrypting anything, 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

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. The current tag is the number 2 encoded as bytes32. Every version moves through a small state machine:
State machine for a commitment version: Unset to Active to Deprecated, with Revoked as the terminal state
Only an Active version accepts new commitments. Revoked is terminal.
The admin-only setVersionStatus(version, newStatus) enforces these transitions and reverts with InvalidVersionTransition otherwise. The transition emits VersionStatusChanged(version, oldStatus, newStatus).

Write surface

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

Both functions batch-write (version, handle) → commitHash rows and require:
  • 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: 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.

Reading commitments

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

Source