This document details the process of integrating Zero-Knowledge (ZK) proofs, generated by a Valence Coprocessor guest program, with the Valence Protocol's on-chain smart contracts. It assumes an understanding of the ZK system as outlined in Valence ZK System Overview and how guest programs are developed as described in Developing Valence Coprocessor Apps.
The core of on-chain integration revolves around submitting the ZK proof and its associated public data to the Authorization.sol contract (for EVM chains), which then collaborates with a VerificationGateway.sol to cryptographically verify the proof's authenticity and correctness.
After your guest program successfully executes on the ZK Coprocessor and a proof is generated, two key pieces of data are essential for on-chain interaction:
- The ZK Proof: This is the raw cryptographic proof data (e.g., SP1 proof bytes) generated by the Coprocessor, attesting to the correct execution of your guest program's ZK circuit.
- The Circuit's Public Output: Your ZK circuit is designed to produce a public output (
Vec<u8>). This output is critical because it represents the data that, once proven correct by the ZK proof, will be used to form theprocessorMessagefor execution by the on-chain ValenceProcessorcontract. It's important to remember that when the full "public inputs" are presented to the on-chain verifier, the first 32 bytes are a prefix that contains the Coprocessor Root hash. The remaining bytes are relevant for constructing on-chain messages.
An off-chain system, such as a script, bot, or backend service, is responsible for retrieving these pieces of data from the Coprocessor (typically after the guest program stores them in its virtual filesystem) and then initiating the on-chain transaction.
To submit a ZK-proven action to the Valence Protocol, the off-chain system must construct a ZKMessage. This structure is specifically designed for the executeZKMessage function within the Authorization.sol contract. The ZKMessage encapsulates all necessary information for the on-chain contracts to process the request:
| Field | Type | Description |
|---|---|---|
registry |
uint64 | Unique identifier for the deployed ZK guest program. Used by the Authorization contract to look up the correct Verification Key (VK) from VerificationGateway. |
blockNumber |
uint64 | Current or recent block number for replay protection. Ensures proofs aren't submitted multiple times, or after that they're no longer relevant. |
authorizationContract |
address | Address of the target Authorization contract. Acts as a safeguard to ensure message is processed by the correct instance. |
processorMessage |
bytes | Core payload dispatched to Processor contract if ZK proof is valid. Derived from circuit's public output and encoded for ProcessorMessageTypes. |
Once the ZKMessage is constructed and the ZK proof is obtained, the off-chain system submits these to the executeZKMessage function of the Authorization.sol contract. The on-chain processing then unfolds as follows:
-
Initial Checks by
Authorization.sol: TheAuthorizationcontract first performs several preliminary checks. It verifies if themsg.sender(the account submitting the transaction) is authorized to provide proofs for the givenregistryID. It also typically checks theblockNumberfrom theZKMessageagainst its record of the last executed block for thatregistryto prevent replay attacks. -
Delegation to
VerificationGateway.sol: If the initial checks pass,Authorization.soldelegates the task of cryptographic proof verification to theVerificationGateway.solcontract whose address it has been configured with. It calls averifyfunction on the gateway, passing along the ZK proof, theregistryID (so the gateway can find the correct VK), and a hash derived from theZKMessagecontents (crucially including theprocessorMessagewhich represents your circuit's output). -
Proof Verification by
VerificationGateway.sol: TheVerificationGatewayretrieves the pre-registered Verification Key (VK) associated with theregistryID. It then uses this VK, the submitted ZK proof, and the public inputs to perform the cryptographic verification. The public inputs are a critical piece of data: they include the Coprocessor Root hash (the first 32 bytes) followed by your circuit's specific output (which is embedded within the hashedZKMessage). This Coprocessor Root hash acts as a commitment to the integrity of all Coprocessor state. It implicitly contains all the embedded state proofs of the domain relevant to that ZK proof, managed via its Sparse Merkle Tree (SMT). Every new block appended to a chain relevant to the proof's domain is included in this SMT with a ZK domain proof, and the verifications of these inclusions are cryptographically embedded into this Coprocessor Root. If the proof is valid for the VK and these comprehensive public inputs, the gateway returns a success status to theAuthorization.solcontract. -
Dispatch to
Processor.sol: If theVerificationGatewayconfirms the proof's validity,Authorization.solconsiders theprocessorMessagewithin theZKMessageto be authentic and authorized for execution. It then typically updates its state for replay protection (e.g., storing theblockNumberas the last executed for thatregistry) and dispatches theprocessorMessageto the appropriate ValenceProcessor.solcontract. -
Execution by
Processor.sol: TheProcessor.solcontract receives theprocessorMessageand executes the sequence of on-chain actions (e.g., calls to various Valence Libraries or other smart contracts) as defined within that message. This is where the result of your ZK-proven off-chain computation translates into tangible on-chain state changes.
This integration pathway ensures that off-chain computations, once proven correct by the ZK Coprocessor, can be securely and reliably acted upon by the Valence on-chain contracts.