Skip to content

Getting Started

The @pcd/gpc package allows you to prove and verify using GPCs. This guide will walk you through how to get started with POD development. The examples below assuming you’re using TypeScript, but the same steps will work from JavaScript as well.

You can find full information about the types and functions used here in the API Reference. If you prefer to read complete working code, check out the tutorial example.

Installation

To get started with GPCs, you will need to install the @pcd/pod package, using your preferred package manager:

npm i @pcd/gpc

The package is available in CJS or ESM format, and will work either in browser or in a server. The example code makes use of bigint literals, so requries a target of at least ES2020.

Packaging for a browser requires polyfill for some Node modules, including buffer and constants, so watch for that if you see any dependency issues.

There is a known issue with a dependency fastfile which can be resolved by polyfilling constants as you can see in this example.

Imports

Next, import the types and functions you need from the package. See the API Reference for everything which is available, and import what you need as you write your code. Here’s an example covering the first few samples below:

gpc.ts
import {
gpcArtifactDownloadURL,
GPCProofConfig, gpcProve,
gpcVerify
} from "@pcd/gpc";

Prove or verify?

The GPC library allows you to create and verify zero knowledge proofs about PODs. Depending on your use case, your app may need to prove, or verify, or both. You may need to so either client-side (in browser) or server-side.

If you intend to integrate with Zupass, you can use the Z-API to query the user’s PODs, and request proofs about them without using the GPC library directly. In that case, you can skip ahead to the verification section of this guide.

This remainder of this guide walks through the steps to both prove and verify in your own app in a browser.

Preparing Inputs

PODs

In order to make a proof, you first need some PODs. The examples below will work with the example POD signed in POD Tutorial. Check out that page first to learn how to work with PODs.

Artifacts

Proving and verifying also requires binary files called artifacts, which are tailored to a specific GPC circuit. The prover and verifier need to use the same artifacts for proofs to be valid. Artifacts released separately from the code since they’re large enough not to be packaged with your app bundle. You can obtain artifacts from NPM or direct download.

See the Circuit Artifacts page for full details about how to manage artifacts. For this example, we’ll fetch them from a CDN backed by NPM.

gpc.ts
const artifactURL = gpcArtifactDownloadURL(
"jsdelivr",
"prod",
undefined
)

Proof Configuration

To make a proof, you first have to say what you want to prove. A GPCProofConfiguration specifies the POD entries you want to prove, and the constraints you want to apply to them. The Proof Configuration page has full details, but let’s start with a simple configuration. This will prove that the ID POD has a birthdate earlier than Nov 11 2003, and reveal the holder’s name.

gpc.ts
const proofConfig: GPCProofConfig = {
pods: {
idcard: {
entries: {
name: { isRevealed: true },
date_of_birth: {
isRevealed: false,
inRange: { min: 0n, max: 1068508800000n }
}
}
}
}
};

Make a proof

With all the preparation in place you can format the inputs to make your first ZK proof! The proofInputs argument lets you match up real PODs with the names used in the configuration.

gpc.ts
const proofInputs = {
pods: {
idcard: myPOD
}
}
const { proof, boundConfig, revealedClaims } = await gpcProve(
proofConfig,
proofInputs,
artifactURL
);

The result of proving includes 3 types of data:

  • proof is the cryptrographic proof of correctness. These are opaque numbers which can be used to verify the correctness of the rest.
  • boundConfig is the same as the configuration you passed in, except it now contains the identifier of the specific circuit which was auto-selected for the proof.
  • revealedClaims contains the information revealed in this proof. In this case, it includes the name entry, as well as the public key of the ID card’s signer.

Transmit the proof as JSON

Usually the prover and verifier won’t be the same app (otherwise there would be no need for a proof). The results of gpcProve() are precisely the values which need to be sent to the verifier.

You can send these objects across the network using JSON. However, they contain non-JSON-compatible types such as bigint so they need to be converted before transmission.

gpc.ts
const proofMessage = JSON.stringify({
proof: proof,
boundConfig: boundConfigToJSON(boundConfig),
revealedClaims: revealedClaimsToJSON(revealedClaims)
});

The verifier can reverse the conversion to recover the original objects. The conversion also fully validates the structure of the objects, and will throw an error if they are malformed.

gpc.ts
const receivedFromProver = JSON.parse(proofMessage);
const proof = receivedFromProver.proof;
const boundConfig = boundConfigFromJSON(receivedFromProver.boundConfig);
const revealedClaims = revealedClaimsFromJSON(receivedFromProver.revealedClaims);

Verify the proof

Finally the verifier can confirm that the proof is valid using gpcVerify(). This needs all three parts of the proof result, as well as the same artifact download URL used by the prover.

gpc.ts
const isValid = gpcVerify(
proof,
boundConfig,
revealedClaims,
artifactURL
);

Check assumptions

If gpcVerify() returns true, you know you have a valid proof of something, but it’s still important to confirm it’s the right proof. The logic inside gpcVerify() ensures that all the contents of the config and revealed claims properly correspond to each other, but it can’t decide whether they match your app’s expectations.

To avoid being fooled by cheating provers, should check:

  • The signer’s public key is the one you expect. Trusted issuers, such as Zupass should publish their public keys.
  • The configuration reflects what you wanted to prove.

Finally you should remember to examine the revealed entries. In this case the user’s name was revealed, so it could be used to record their admittance to an event.