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.sol
contract (for EVM chains), which then collaborates with a VerificationGateway.sol
to cryptographically verify the proof's authenticity and correctness.
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 theprocessorMessage
for execution by the on-chain ValenceProcessor
contract. 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.
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. 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. |
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 by
Authorization.sol
: TheAuthorization
contract first performs several preliminary checks. It verifies if themsg.sender
(the account submitting the transaction) is authorized to provide proofs for the givenregistry
ID. It also typically checks theblockNumber
from theZKMessage
against its record of the last executed block for thatregistry
to prevent replay attacks. -
Delegation to
VerificationGateway.sol
: If the initial checks pass,Authorization.sol
delegates the task of cryptographic proof verification to theVerificationGateway.sol
contract whose address it has been configured with. It calls averify
function on the gateway, passing along the ZK proof, theregistry
ID (so the gateway can find the correct VK), and a hash derived from theZKMessage
contents (crucially including theprocessorMessage
which represents your circuit's output). -
Proof Verification by
VerificationGateway.sol
: TheVerificationGateway
retrieves the pre-registered Verification Key (VK) associated with theregistry
ID. 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.sol
contract. -
Dispatch to
Processor.sol
: If theVerificationGateway
confirms the proof's validity,Authorization.sol
considers theprocessorMessage
within theZKMessage
to be authentic and authorized for execution. It then typically updates its state for replay protection (e.g., storing theblockNumber
as the last executed for thatregistry
) and dispatches theprocessorMessage
to the appropriate ValenceProcessor.sol
contract. -
Execution by
Processor.sol
: TheProcessor.sol
contract receives theprocessorMessage
and 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.