Verifiable AI inference directly on blockchain networks is increasingly in demand, especially for trustless DeAI and DeFi systems requiring cryptographic proof that AI computations happened correctly. zkML is an emerging technique enabling zero-knowledge verifiable machine learning inference, which ensures both privacy and integrity of AI results.
Ezkl is an open-source framework designed to facilitate this by converting neural networks into zk-SNARK circuits, enabling production-ready zero-knowledge proofs that can be verified on-chain efficiently. In this tutorial, I’ll walk through setting up Ezkl, converting a simple AI model (built in PyTorch) into a zero-knowledge proof circuit, generating proofs off-chain, and verifying them on-chain with Solidity contracts.
What intrigued me at first was how ezkl abstracts much of the cryptographic complexity while supporting widely used ML formats like ONNX, making the learning curve less steep for Solidity and AI developers alike.
Performing AI inference on-chain is tricky due to computation limits and gas costs. Instead of executing AI models on-chain, zkML enables off-chain inference paired with succinct zkSNARK proofs attesting to the correctness of the inference.
This guarantees that a verified result—say, a DeFAI agent’s decision or a DePIN sensor’s classification—actually stems from authentic model execution without revealing the input data or model internals.
Some concrete examples:
Without verifiable evidence, these models would require implicit trust, risking oracle/data manipulation. zkML breaks this trust assumption by cryptographic means.
Let me start by outlining prerequisites and environment setup. For this example, I used ezkl version 0.5.1 (check official repo for updates).
Prerequisites:
Install ezkl CLI via Cargo:
cargo install ezkl
Then clone the sample model repo or create your own PyTorch model (next section).
I suggest using a virtualenv and pinning all dependencies for a repeatable environment. Also, run tests locally before deploying on testnet/mainnet due to gas costs.
Ezkl supports ONNX format as the intermediary to generate zk-SNARK circuits. So the pipeline goes:
PyTorch (or other ML framework) → export model → ONNX → ezkl circuit
Here's a basic PyTorch model for XOR classification:
import torch
import torch.nn as nn
class XORModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(2, 4)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(4, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.sigmoid(self.fc2(x))
return x
model = XORModel()
model.eval()
# Export sample input
dummy_input = torch.tensor([[0., 0.]])
torch.onnx.export(model, dummy_input, "xor.onnx", opset_version=12)
Save this code as export_xor.py and run it to get xor.onnx.
This ONNX model can then be fed into ezkl.
Generate the circuit with:
ezkl compile xor.onnx --output xor
This produces circuit files to the xor directory, representing the arithmetic constraints for zkSNARK proof.
I remember first trying this and hitting opset mismatches — opset 12 works well with ezkl right now, but always confirm with the latest docs.
Proof generation happens off-chain using the circuit and the supplied inputs. Ezkl comes with Rust APIs and CLI to facilitate this.
Suppose you want to prove that the input [1, 0] produces output near 1 in the XOR model.
Create a JSON input file input.json:
{
"0": [1, 0]
}
Run proof generation:
ezkl prove --circuit xor/circuit.json --input input.json --prover xor/prover
This produces a proof file (proof.json) which contains the zkSNARK proof data.
What’s nice here is that Ezkl supports reproducible proof generation and optimizing performance by reusing keys.
Now to the Solidity part. Ezkl auto-generates a Solidity verifier contract from the circuit:
ezkl verifier --circuit xor/circuit.json --output contracts/XorVerifier.sol
The Solidity contract exposes a verifyProof function typical of zkSNARK verifier contracts:
function verifyProof(
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[] memory input
) public view returns (bool) {
// auto-generated verifier logic
}
Deploy this contract on your testnet, then call this function passing proof parameters serialized from proof.json.
Here's a snippet in Hardhat (TypeScript) for invoking the verifier:
const proof = require('./proof.json');
const verifier = await ethers.getContractAt('XorVerifier', verifierAddress);
const verified = await verifier.verifyProof(
proof.proof.a,
proof.proof.b,
proof.proof.c,
proof.publicSignals
);
console.log(`Verified: ${verified}`);
If everything checks out, the verifier will return true, confirming the on-chain verifiable proof for the AI inference.
There’s often confusion around zkML vs OPML (Oracle Proof ML) vs Trusted Execution Environments (TEE) when implementing verifiable AI. Here’s how I differentiate them:
| Feature / Aspect | zkML | opML | TEE |
|---|---|---|---|
| Proof Type | zk-SNARK zero-knowledge proofs | Oracle-based proofs | Hardware attestation |
| On-chain Verification | Yes, succinct and trustless | Yes, but relies on oracle trust model | No (usually off-chain verification) |
| Data Privacy | Strong (input/output obfuscation) | Medium (depends on oracle) | Medium (HW security boundary) |
| Performance Overhead | High off-chain proof generation | Lower (oracle submits proof) | Low but hardware-dependent |
| Threat Model | Cryptographic soundness | Requires trusted oracle infrastructure | Vulnerable to side-channels |
In projects aiming for minimal trust assumptions and privacy (like DeFAI agents on EVM chains), zkML via Ezkl shines. But if you need lower latency or lack zk-tooling expertise, opML or hardware TEEs can be options. And of course, hybrid models also exist.
If you want a deeper dive into oracle-centric approaches, see the related ora-protocol-opml-integration page.
Now, some concrete security insights from my experience integrating zkML into Solidity workflows:
These security footguns are easy to overlook until they bite in production. Personally, I run automated audit tools like Slither and Aderyn on the verifier contracts to catch low-hanging vulnerabilities.
This tutorial showcased how to go from standard PyTorch models to verifiable AI inference on-chain using zkML with Ezkl — covering environment setup, conversion to ONNX, zkSNARK proof generation, and Solidity verification.
The tooling is still evolving. Proof generation times and gas usage remain a challenge, especially for complex networks, but the promise of cryptographically sound DeFAI is compelling for trustless applications.
If you want to automate security audits or implement continuous verification pipelines for your zkML-based contracts, check out our smart-contract-ci-cd-pipeline guide.
Got errors with Ezkl CLI flags or verifier deployment? Our automated-solidity-security-audit and solidityscan-ai-vulnerability-detection pages have practical troubleshooting tips.
Finally, curious about the cryptographic details of how Ezkl circuits find constraints and why Rust is the chosen language? The source code and docs offer a treasure trove, but I believe hands-on testing is still the fastest way to mastery.
What’s your take? Have you tried zkML on your own AI agents or DeFAI protocols? Drop your experience or questions — and happy proving!