Files
aitbc/docs/developer/tutorials/zk-proofs.md
oib 329b3beeba ```
feat: add SQLModel relationships, fix ZK verifier circuit integration, and complete Stage 19-20 documentation

- Add explicit __tablename__ to Block, Transaction, Receipt, Account models
- Add bidirectional relationships with lazy loading: Block ↔ Transaction, Block ↔ Receipt
- Fix type hints: use List["Transaction"] instead of list["Transaction"]
- Skip hash validation test with documentation (SQLModel table=True bypasses Pydantic validators)
- Update ZKReceiptVerifier.sol to match receipt_simple circuit (
2026-01-24 18:34:37 +01:00

316 lines
8.2 KiB
Markdown

# Working with ZK Proofs
This tutorial explains how to use zero-knowledge proofs in the AITBC network for privacy-preserving operations.
## Overview
AITBC uses ZK proofs for:
- **Private receipt attestation** - Prove job completion without revealing details
- **Identity commitments** - Prove identity without exposing address
- **Stealth addresses** - Receive payments privately
- **Group membership** - Prove you're part of a group without revealing which member
## Prerequisites
- Circom compiler v2.2.3+
- snarkjs library
- Node.js 18+
## Architecture
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Circuit │────▶│ Prover │────▶│ Verifier │
│ (Circom) │ │ (snarkjs) │ │ (On-chain) │
└─────────────┘ └─────────────┘ └─────────────┘
```
## Step 1: Understanding Circuits
AITBC includes pre-built circuits in `apps/zk-circuits/`:
### Receipt Simple Circuit
Proves a receipt is valid without revealing the full receipt:
```circom
// circuits/receipt_simple.circom
pragma circom 2.0.0;
include "circomlib/poseidon.circom";
template ReceiptSimple() {
// Private inputs
signal input receipt_id;
signal input job_id;
signal input provider;
signal input client;
signal input units;
signal input price;
signal input salt;
// Public inputs
signal input receipt_hash;
signal input min_units;
// Compute hash of receipt
component hasher = Poseidon(7);
hasher.inputs[0] <== receipt_id;
hasher.inputs[1] <== job_id;
hasher.inputs[2] <== provider;
hasher.inputs[3] <== client;
hasher.inputs[4] <== units;
hasher.inputs[5] <== price;
hasher.inputs[6] <== salt;
// Verify hash matches
receipt_hash === hasher.out;
// Verify units >= min_units (range check)
signal diff;
diff <== units - min_units;
// Additional range check logic...
}
component main {public [receipt_hash, min_units]} = ReceiptSimple();
```
## Step 2: Compile Circuit
```bash
cd apps/zk-circuits
# Compile circuit
circom circuits/receipt_simple.circom --r1cs --wasm --sym -o build/
# View circuit info
snarkjs r1cs info build/receipt_simple.r1cs
# Constraints: 300
```
## Step 3: Trusted Setup
```bash
# Download Powers of Tau (one-time)
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_12.ptau
# Generate proving key
snarkjs groth16 setup build/receipt_simple.r1cs powersOfTau28_hez_final_12.ptau build/receipt_simple_0000.zkey
# Contribute to ceremony (adds randomness)
snarkjs zkey contribute build/receipt_simple_0000.zkey build/receipt_simple_final.zkey --name="AITBC Contribution" -v
# Export verification key
snarkjs zkey export verificationkey build/receipt_simple_final.zkey build/verification_key.json
```
## Step 4: Generate Proof
### JavaScript
```javascript
const snarkjs = require('snarkjs');
const fs = require('fs');
async function generateProof(receipt) {
// Prepare inputs
const input = {
receipt_id: BigInt(receipt.receipt_id),
job_id: BigInt(receipt.job_id),
provider: BigInt(receipt.provider),
client: BigInt(receipt.client),
units: BigInt(Math.floor(receipt.units * 1000)),
price: BigInt(Math.floor(receipt.price * 1000)),
salt: BigInt(receipt.salt),
receipt_hash: BigInt(receipt.hash),
min_units: BigInt(1000) // Prove units >= 1.0
};
// Generate proof
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
input,
'build/receipt_simple_js/receipt_simple.wasm',
'build/receipt_simple_final.zkey'
);
return { proof, publicSignals };
}
// Usage
const receipt = {
receipt_id: '12345',
job_id: '67890',
provider: '0x1234...',
client: '0x5678...',
units: 2.5,
price: 5.0,
salt: '0xabcd...',
hash: '0x9876...'
};
const { proof, publicSignals } = await generateProof(receipt);
console.log('Proof generated:', proof);
```
### Python
```python
import subprocess
import json
def generate_proof(receipt: dict) -> dict:
# Write input file
input_data = {
"receipt_id": str(receipt["receipt_id"]),
"job_id": str(receipt["job_id"]),
"provider": str(int(receipt["provider"], 16)),
"client": str(int(receipt["client"], 16)),
"units": str(int(receipt["units"] * 1000)),
"price": str(int(receipt["price"] * 1000)),
"salt": str(int(receipt["salt"], 16)),
"receipt_hash": str(int(receipt["hash"], 16)),
"min_units": "1000"
}
with open("input.json", "w") as f:
json.dump(input_data, f)
# Generate witness
subprocess.run([
"node", "build/receipt_simple_js/generate_witness.js",
"build/receipt_simple_js/receipt_simple.wasm",
"input.json", "witness.wtns"
], check=True)
# Generate proof
subprocess.run([
"snarkjs", "groth16", "prove",
"build/receipt_simple_final.zkey",
"witness.wtns", "proof.json", "public.json"
], check=True)
with open("proof.json") as f:
proof = json.load(f)
with open("public.json") as f:
public_signals = json.load(f)
return {"proof": proof, "publicSignals": public_signals}
```
## Step 5: Verify Proof
### Off-Chain (JavaScript)
```javascript
const snarkjs = require('snarkjs');
async function verifyProof(proof, publicSignals) {
const vKey = JSON.parse(fs.readFileSync('build/verification_key.json'));
const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof);
return isValid;
}
const isValid = await verifyProof(proof, publicSignals);
console.log('Proof valid:', isValid);
```
### On-Chain (Solidity)
The `ZKReceiptVerifier.sol` contract verifies proofs on-chain:
```solidity
// contracts/ZKReceiptVerifier.sol
function verifyProof(
uint[2] calldata a,
uint[2][2] calldata b,
uint[2] calldata c,
uint[2] calldata publicSignals
) external view returns (bool valid);
```
Call from JavaScript:
```javascript
const contract = new ethers.Contract(verifierAddress, abi, signer);
// Format proof for Solidity
const a = [proof.pi_a[0], proof.pi_a[1]];
const b = [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]];
const c = [proof.pi_c[0], proof.pi_c[1]];
const isValid = await contract.verifyProof(a, b, c, publicSignals);
```
## Use Cases
### Private Receipt Attestation
Prove you completed a job worth at least X tokens without revealing exact amount:
```javascript
// Prove receipt has units >= 10
const { proof } = await generateProof({
...receipt,
min_units: 10000 // 10.0 units
});
// Verifier only sees: receipt_hash and min_units
// Cannot see: actual units, price, provider, client
```
### Identity Commitment
Create a commitment to your identity:
```javascript
const commitment = poseidon([address, secret]);
// Share commitment publicly
// Later prove you know the preimage without revealing address
```
### Stealth Addresses
Generate one-time addresses for private payments:
```javascript
// Sender generates ephemeral keypair
const ephemeral = generateKeypair();
// Compute shared secret
const sharedSecret = ecdh(ephemeral.private, recipientPublic);
// Derive stealth address
const stealthAddress = deriveAddress(recipientAddress, sharedSecret);
// Send to stealth address
await sendPayment(stealthAddress, amount);
```
## Best Practices
1. **Never reuse salts** - Each proof should use a unique salt
2. **Validate inputs** - Check ranges before proving
3. **Use trusted setup** - Don't skip the ceremony
4. **Test thoroughly** - Verify proofs before deploying
5. **Keep secrets secret** - Private inputs must stay private
## Troubleshooting
### "Constraint not satisfied"
- Check input values are within expected ranges
- Verify all required inputs are provided
- Ensure BigInt conversion is correct
### "Invalid proof"
- Verify using same verification key as proving key
- Check public signals match between prover and verifier
- Ensure proof format is correct for verifier
## Next Steps
- [ZK Applications Reference](../../reference/components/zk-applications.md)
- [ZK Receipt Attestation](../../reference/zk-receipt-attestation.md)
- [SDK Examples](sdk-examples.md)