Generate a ZK Proof

View as Markdown
When to use

You want to share a “I am over 18” / “I’m a verified European resident” / “I have ≥ 5 BTC” proof without revealing the underlying credential. The portal generates the proof in your browser (or, optionally, server-side if your client cannot run the prover).

Before you begin
  • A credential containing the relevant claim (e.g., a KYC credential with dateOfBirth for age_gte).
  • The verifier-supplied challenge (or generate one from the API).
Steps
  1. Open /zk-identity.
  2. Click Generate proof and pick a predicate from the dropdown. 3. Fill predicate-specific parameters (e.g. for age_gte, the threshold = 18).
  3. Select the source credential.
  4. Click Generate. The portal:
    • Calls POST /api/v1/zkp/challenge to obtain a fresh nonce.
    • Builds the witness from the credential’s claims + your private signing share.
    • Runs the prover (in-browser via WASM, or server-side fallback).
    • Calls POST /api/v1/zkp/proofs to register the proof in the platform.
  5. The portal displays the proof ID and a copy/share button.
API & SDK
cURL — challengecURL — submit
$curl -X POST https://adid.dev/api/v1/zkp/challenge \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{ "predicate":"age_gte", "params":{"threshold":18} }'
$curl -X POST https://adid.dev/api/v1/zkp/proofs \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "predicate":"age_gte",
> "challenge":"f9c8b3e0...",
> "proof":"<hex>",
> "proverDid":"did:adi:0x9a2c...",
> "credentialId":"urn:uuid:c5b7e3..."
> }'
1const { challenge } = await client.generateZKChallenge();
2const stored = await client.generateZKProof({
3 proverDid: myDid,
4 credentialId: theCredential.id,
5 predicates: [{ type: 'age_gte', field: 'birthDate', value: 18 }],
6 challenge,
7});
Verify
$curl https://adid.dev/api/v1/zkp/proofs/<id> \
> -H "Authorization: Bearer $ACCESS_TOKEN"

shows status: "verified" and the predicate / params / credentialId.

Troubleshooting
CodeCauseFix
410 CHALLENGE_EXPIREDMore than 5 min between challenge and proof submitRequest a fresh challenge.
422 PREDICATE_NOT_SATISFIEDThe credential’s claim does not satisfy the predicate (e.g. age < threshold)Use a different predicate, or accept that you do not satisfy.
500 PROVER_ERRORBrowser ran out of memoryUse the server-side fallback (toggle in dev tools).

3.3.4. Sharing & Anchoring Proofs On-Chain ##### When to use

The verifier requires a public, immutable record of your proof — typically for high-stakes use (regulatory KYC, on-chain DAO voting). On-chain anchoring is opt-in.

Before you begin
  • A generated proof (§3.3.3).
  • (Optional) wallet permission to sign the chain transaction.
Steps
  1. From the proof’s detail page, click Anchor on chain.
  2. The wallet prompts for confirmation. Approve.
  3. The wallet calls submitProof(proofHash, proverDID, credentialId, predicateHash) on the ZKProofVerifier contract (see §11.6).
  4. After receipt, the proof’s record on the platform shows the txHash and onChain: true.
Sharing

To share a proof off-chain, click Copy share link — generates a URL like https://adid.dev/proofs/abc123 that any party can visit (no auth needed) to verify.

API
cURL — verify
$curl -X POST https://adid.dev/api/v1/zkp/verify \
> -H "Content-Type: application/json" \
> -d '{
> "proof":"<hex>",
> "predicate":"age_gte",
> "params":{"threshold":18},
> "challenge":"f9c8b3e0...",
> "proverDid":"did:adi:0x9a2c..."
> }'

(No auth — /api/v1/zkp/verify is public per router.go:71.)

Verify

After anchoring, query verifyProofOnChain(proofHash) on the contract via the API:

$curl https://adid.dev/api/v1/zkp/proofs/<id>?includeOnChain=true \
> -H "Authorization: Bearer $ACCESS_TOKEN"
Troubleshooting
CodeCauseFix
409 ALREADY_ANCHOREDThis proof’s hash is already on chainNo action needed.
503 CHAIN_UNREACHABLERPC downRetry.
403 NOT_PROVERYou are not the original proverOnly the prover may anchor / revoke.

⚠️ Warning — On-chain anchoring is permanent. Proof revocation is supported (call revokeProof(proofHash)) but the original anchor record remains visible (just with revoked: true). Do not anchor a proof unless you intend it to be auditable forever.