Chapter 10 / 15
Zero-Knowledge Proofs
selective_disclosure, verified_by — proving properties without revealing data.
ZK proofs let a contract verify a claim — "the user is over 18", "the balance
exceeds 1000 USDC", "the merkle path is valid" — without the contract ever
seeing the underlying data. Covenant's zk_proof type and
verified_by clause compile to STATICCALL into the chain's ZK
precompile (0x0500–0x0501).
age_gate.cov — prove age without revealing it
interface IZKVerifier {
// Returns true if proof is valid for the given public inputs
view verify(proof: zk_proof, inputs: [field; 4]) -> bool;
}
record AgeGate {
verifier: address; // chain-deployed ZK verifier precompile
min_age: u32 = 18;
members: map(address => bool);
error ProofInvalid();
// User proves age >= 18 without revealing birth date
// Public inputs: [min_age, current_year, 0, 0]
action join(proof: zk_proof) {
let ok = IZKVerifier(self.verifier).verify(
proof,
[self.min_age as field, block.timestamp / 31_536_000 as field, 0, 0]
);
if !ok {
revert_with ProofInvalid();
}
self.members[msg.sender] = true;
}
view is_member(who: address) -> bool {
return self.members[who];
}
}merkle_drop.cov — airdrop via Merkle proof
record MerkleDrop {
root: hash;
token: address;
claimed: map(address => bool);
error AlreadyClaimed();
error InvalidProof();
// Claim tokens by proving inclusion in the Merkle tree
action claim(amount: u256, proof: [hash; 32]) {
if self.claimed[msg.sender] {
revert_with AlreadyClaimed();
}
// Reconstruct leaf = keccak256(address ++ amount)
let leaf = keccak256(msg.sender, amount);
// Verify Merkle path
if !merkle_verify(leaf, proof, self.root) {
revert_with InvalidProof();
}
self.claimed[msg.sender] = true;
IERC20(self.token).transfer(msg.sender, amount);
}
}Annotations
zk_proof | is an opaque byte-array type. Its validity is verified by the ZK precompile, not Covenant itself. |
field | is a finite-field element (BN254 scalar by default). ZK circuits express inputs as field elements. |
merkle_verify | is a Covenant built-in that compiles to the ZK precompile's Merkle path verification entry-point. |
| Nova IVC vs Halo2 | Nova is preferred for recursive proofs (incrementally verifiable computation); Halo2 for single-shot range/inclusion proofs. Chain precompile support varies. |
Key takeaways
- ZK proofs let you verify properties of private data without exposing the data itself.
- Covenant's
zk_prooftype is chain-agnostic — the proof system is selected by the precompile, not the language. - Merkle airdrops, age gates, and range proofs are the most common ZK patterns in production.