A claim type is the specific assertion a zero-knowledge proof makes about a credential — e.g. "this credential holder has a degree" — without revealing the credential's underlying metadata. This is distinct from the credential type registry, which classifies what a credential itself is (a degree, a license, an employment record). A single credential type is typically proved against one or more claim types.
Claim types are consumed by generate_proof_request /
generate_anonymous_proof_request and verify_claim in
contracts/zk_verifier, and mirrored in contracts/quorum_proof so the two
contracts can pass the same value across the contract boundary (see the
#[repr(u32)] note in contracts/zk_verifier/src/lib.rs — the two enums
must stay byte-identical or cross-contract calls fail with a
ConversionError).
This document defines the canonical, standardized set of claim types, the proof circuit design behind each, the registry's versioning strategy, and the process for proposing a new one.
ClaimType is a fixed, #[repr(u32)] enum — not an arbitrary string —
defined identically in contracts/quorum_proof/src/lib.rs and
contracts/zk_verifier/src/lib.rs:
| Variant | Discriminant | Domain-separation index | Status |
|---|---|---|---|
HasDegree |
1 | 0 | Standard |
HasLicense |
2 | 1 | Standard |
HasEmploymentHistory |
3 | 2 | Standard |
HasCertification |
4 | 3 | Standard |
HasResearchPublication |
5 | 4 | Standard |
The "domain-separation index" is the value each claim type maps to
internally (ClaimType::HasDegree => 0u8, etc. in zk_verifier::lib.rs) and
is mixed into the Fiat-Shamir transcript / public-input encoding so a proof
generated for one claim type cannot be replayed as valid for another. See
ZK Proof Scheme Specification and
PLONK Verification for the full transcript
construction.
Note on scope vs. the original proposal: this issue's brief called for
has_degree,has_license,experience_years, andemployer_verification. Two of those (HasDegree,HasLicense) already exist as-is.experience_yearsandemployer_verificationdo not exist as separate variants today — the closest existing type isHasEmploymentHistory, which proves that an employment credential exists and is valid, not a minimum-years threshold or a specific-employer match. Adding either as a true standard type requires a new circuit (see Custom / Proposed Claim Types below) and is not implemented in this change — this document defines the process so that work can proceed as its own scoped task.
All standard claim types share one circuit shape — a membership /
validity proof over a committed credential — differing only in which fields
of the credential's metadata commitment are constrained. verify_claim
takes (admin, quorum_proof_contract, credential_id, claim_type, proof);
claim_type selects the constraint set applied to proof against the
on-chain credential commitment. See
ZK API Reference for the full parameter reference and
Groth16 → PLONK Migration for the current backend.
- Statement: the subject holds a valid, non-revoked credential of type
Degree(or a descendant in the credential type hierarchy) issued by an authorized issuer. - Public inputs: credential type root ID, issuer commitment, revocation root, nonce.
- Private witness: full credential metadata (institution, field of study, graduation date), Merkle path to the credential commitment.
- What is not revealed: institution name, GPA, exact graduation date.
- Statement: the subject holds a valid, non-expired professional license
credential, optionally scoped to a specific
license_type(see credential-types.md, Professional License hierarchy) passed as an additional circuit parameter. - Public inputs: as above, plus
current_ledger_timestampso expiry can be checked without revealing the exactexpiry_date. - Private witness: license number, jurisdiction, discipline, issuing authority, expiry date.
- What is not revealed: license number, exact jurisdiction/discipline unless the verifier explicitly requested a scoped proof.
- Statement: the subject holds at least one valid Employment History credential from an attested employer.
- Public inputs: credential type root ID, issuer (employer) commitment.
- Private witness: employer identity, role, dates of employment.
- What is not revealed: employer identity, role, or duration — this is
existence-only. It does not prove a minimum tenure or a specific
employer; see
experience_years/employer_verificationbelow for how those stronger statements would extend this circuit.
- Statement: the subject holds a valid, non-revoked Certificate-type credential.
- Public inputs / witness: same shape as
HasDegree, scoped to the Certificate branch of the credential type hierarchy.
- Statement: the subject holds a valid credential attesting authorship or co-authorship of a research publication.
- Public inputs / witness: same shape as
HasDegree, scoped to a research-publication credential type.
ClaimType is a compiled #[repr(u32)] Rust enum shared by two contracts,
not a runtime-registrable value like the credential type registry — so
its versioning rules are stricter:
- Additive only, in-place. New claim types are added as new enum
variants with the next unused discriminant (currently
6is next). Existing discriminants are never reused or renumbered — averify_claimcall with a stale discriminant must always resolve to the same claim type it did when issued, indefinitely. - No removals. A claim type is never deleted, only marked
Deprecatedin this document, to preserve verifiability of proofs generated against it in the past. - Both contracts move together. Because
quorum_proofandzk_verifiermirror this enum for cross-contract ABI compatibility (see the safety comment at the top of the enum incontracts/zk_verifier/src/lib.rs), a new variant must be added to both files in the same change and deployed together. A mismatch between the two is a deploy-time correctness bug, not just a docs gap. - Circuit changes require a new type, not a mutation. If a claim's constraint set changes in a way that alters what a proof attests to, ship it as a new variant rather than changing what an existing discriminant means — old proofs must keep verifying against the semantics they were generated under.
- This document is the source of truth for semantics. The enum defines the wire values; this file defines what each one means. Update this table in the same PR that adds a variant.
| Registry version | Change |
|---|---|
| 1.0 (current) | HasDegree, HasLicense, HasEmploymentHistory, HasCertification, HasResearchPublication |
To propose a new standard claim type (e.g. experience_years,
employer_verification):
- Open an issue describing the statement the claim should prove, and explicitly what it must not reveal.
- Design the circuit: define public inputs, private witness, and how
the statement is constrained (e.g.
experience_yearsneeds a range proof over a computedend_date - start_date, which is a materially different circuit from the existence-only proofs above; a threshold parameter such as "at least 5 years" becomes an additional public input). Cross-reference against ZK Proof Scheme Specification for the constraint primitives already available in the circuit. - Security review: run the new circuit design past Threat Model & Security Analysis and Security Audit Checklist — a new proof circuit is new attack surface (soundness/completeness of the constraints, information leakage through public inputs).
- Implement as a new discriminant in both
quorum_proof::ClaimTypeandzk_verifier::ClaimTypeper the versioning rules above, with tests in both contracts and incontracts/integration_tests. - Update this document with the new type's row in the canonical table and a full "Proof Circuit Design" subsection, and bump the registry version above.
Until a proposed type completes this process and lands as a real enum
variant, it is not a valid claim_type value — generate_proof_request
only accepts the five standard types listed above.