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

  1. Fetch pricePerTokenWei for the model from the billing contract.
  2. Generate a proof with private token counts and a random nonce.
  3. Submit chargeWithProof with the proof, usageHash, and nullifier.
  4. The verifier checks the proof; the billing contract checks price and replay protection.
  5. 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.