Integrating ZK Proofs with On-Chain Contracts
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 contract (CosmWasm or EVM), which then collaborates with a VerificationRouter to cryptographically verify the proof's authenticity and correctness. For SP1 proofs, the system uses an SP1VerificationSwitch that performs dual verification: validating both the program proof (using the provided VK) and the domain proof (using the Coprocessor root commitments).
Preparing Data for On-Chain Submission
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 Valence Processor contract. When the full "public inputs" are presented to the on-chain verifier, the first 32 bytes contain the Coprocessor Root (historical commitments). The remaining bytes are the circuit output your app defines. For how domain and historical openings bind values to this root, see Domain Proofs.
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.
The ZKMessage Structure
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. Maps to ZK authorization registry in the Authorization contract. |
blockNumber | uint64 | Current or recent block number for replay protection. Prevents reuse of old proofs if validateBlockNumberExecution is enabled for the registry. |
authorizationContract | address | Address of the target Authorization contract. Can be address(0) to allow any authorization contract, or specific address for binding. |
processorMessage | bytes | Core payload dispatched to Processor contract if ZK proof is valid. Contains the actual message to be executed, derived from the circuit's public output. |
On-Chain Verification Sequence
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: The Authorization contract first performs several preliminary checks. It verifies if the
msg.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 VerificationRouter: If the initial checks pass, the Authorization contract delegates the task of cryptographic proof verification to the VerificationRouter contract whose address it has been configured with using a
route. It calls averifyfunction on the router, passing along the ZK proof, the verifying key (VK), the public inputs for the proof and a payload. -
Proof Verification: The VerificationRouter retrieves the verifier associated by the route and delegates the verification to that verifier. For SP1 proofs, the
SP1VerificationSwitchperforms dual verification:- Program Proof: Uses the provided VK to verify the circuit's computation with your specific public inputs
- Domain Proof: Uses the stored
domainVKto verify the first 32 bytes (coprocessor root hash) which acts as a commitment to all Coprocessor state integrity
The Coprocessor Root hash implicitly contains all embedded state proofs of domains relevant to the ZK proof, managed via a Sparse Merkle Tree (SMT). Every new block appended to chains 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 root. Both proofs must pass for successful verification. If valid, the router returns a success status to the Authorization contract.
-
Dispatch to Processor: If the VerificationRouter confirms the proof's validity, the Authorization contract considers the
processorMessagewithin 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 Valence Processor contract. -
Execution by Processor: The Processor contract receives the
processorMessageand 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.
Verifying Keys
- Guest program VKs can be fetched from the Coprocessor via
GET /api/circuit/vk(base64) for a given controller context. - For recursive domain proofs, a domain prover service publishes a stable wrapper VK (e.g., via
/api/consts). On‑chain verifiers can bind to this VK and the expected controller ID.