SDK Reference
generateProof()
Run the Althea Groth16 circuit inside the user's browser. The circuit validates the credential, the age, and the ID type - then outputs a zero-knowledge proof and a nullifier. The credential is wiped from memory before this function returns.
Signature
generateProof(credential: SignedCredential): Promise<ZKProof>What this function does
- Validates the credential object shape.
- Loads afrzk.wasm from the SDK bundle (≈ 240 KB, cached after first load).
- Runs snarkjs.groth16.fullProve() with the credential as private input.
- The circuit validates: signature, age ≥ 18, and idType is 1 or 2.
- Verifies the proof locally with snarkjs.groth16.verify() before returning. A circuit bug or tampered WASM is caught here.
- Wipes the credential from memory - sets every field to zero - so the original NIN hash cannot be recovered.
- Returns the proof and public signals.
Proof generation is expensive
Groth16 proof generation takes 3–8 seconds depending on device. Show a clear loading state. Do not call generateProof() more than once per verification flow - fail loudly if a user tries to retry without restarting from verifyIdentity().Returns
A ZKProof object containing a Groth16 proof and the public signals the verifier checks.
ZKProof
type ZKProof = {
proof: {
pi_a: [string, string, string]
pi_b: [[string, string], [string, string], [string, string]]
pi_c: [string, string, string]
protocol: 'groth16'
curve: 'bn128'
}
publicSignals: [string, string]
// [0] = valid (1 if all circuit checks passed, 0 otherwise)
// [1] = nullifier (one-way hash, deduplication key)
}Public signals
| Index | Value | Description |
|---|---|---|
| 0 | '1' | '0' | valid — '1' means all three in-circuit checks passed |
| 1 | string | nullifier — a Poseidon hash of the idHash; unique per identity, unlinkable to the underlying ID |
Example
generate-proof-example.ts
import { verifyIdentity, generateProof } from 'afrzk-sdk'
const credential = await verifyIdentity({
idType: 'NIN',
idNumber: nin,
dob,
})
const proof = await generateProof(credential)
// credential fields have been zeroed and the object reference is no longer
// safe to use - it is a wiped husk by design.
if (proof.publicSignals[0] !== '1') {
throw new Error('Circuit rejected the credential')
}
console.log('Nullifier:', proof.publicSignals[1])Errors
| Message | Cause |
|---|---|
| Invalid credential shape | credential is missing fields or has wrong types |
| Circuit constraints failed | Age < 18, signature invalid, or idType is not 1 or 2 |
| WASM failed to load | Network/blob fetch error or CSP blocking wasm-unsafe-eval |
| Local verification failed | Generated proof does not verify — indicates a corrupt circuit; do NOT submit |
Performance benchmarks
| Device class | Approximate proof time |
|---|---|
| Desktop (M1+ / Ryzen 5+) | 1.8s – 2.4s |
| Modern laptop (Intel 11th gen) | 3.0s – 4.0s |
| Mid-tier Android (2022) | 4.5s – 6.5s |
| Older Android (2018) | 7.5s – 11s |
See also
- submitProof() - submit the generated proof on-chain.
- The Circuit - what the constraints actually check.