ZK Billing Proofs
Overview
Harpocrates uses a Groth16 proof to authorize charges on-chain without revealing token counts. The proof shows that the cost was computed correctly from private usage data and binds that usage to a public commitment.
Settlement stays ETH-denominated on Horizen L3 while ZEN is not yet available on that network.
Circuit Semantics
- Private inputs: inputTokens, outputTokens, userSecret, nonce.
- Public inputs: user, modelId, pricePerTokenWei, costWei, usageHash, nullifier (split into 128-bit limbs).
- Constraints: cost = (input + output) * price, usageHash = Poseidon(user, modelId, inputTokens, outputTokens, nonce), nullifier = Poseidon(userSecret, usageHash).
On-Chain Flow
- Fetch pricePerTokenWei for the model from the billing contract.
- Generate a proof with private token counts and a random nonce.
- Submit chargeWithProof with the proof, usageHash, and nullifier.
- The verifier checks the proof; the billing contract checks price and replay protection.
- Balances are debited in ETH and the receipt event is emitted.
The demo server submits chargeWithProof using a billing signer (BILLING_PRIVATE_KEY). Keep this key server-side and do not expose it to clients.
Replay Protection
Each proof includes a nullifier derived from a user secret and the usageHash. The contract stores nullifierUsed to prevent double charges from the same proof.
This proof hides token counts on-chain, but the modelId, price, and total cost remain public for settlement. Metadata such as timing and model selection are not hidden.