Skip to content

Commit

Permalink
Initial version of POD Email PCD
Browse files Browse the repository at this point in the history
  • Loading branch information
robknight committed Jan 17, 2025
1 parent e8553f2 commit dcd8f49
Show file tree
Hide file tree
Showing 13 changed files with 1,132 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/pcd/pod-email-pcd/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
extends: ["@pcd/eslint-config-custom"],
root: true,
};
5 changes: 5 additions & 0 deletions packages/pcd/pod-email-pcd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.js
*.ts.map
!.eslintrc.js
dist
*.tsbuildinfo
674 changes: 674 additions & 0 deletions packages/pcd/pod-email-pcd/LICENSE

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions packages/pcd/pod-email-pcd/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "@pcd/pod-email-pcd",
"private": true,
"version": "0.0.0",
"license": "GPL-3.0-or-later",
"main": "./dist/cjs/src/index.js",
"module": "./dist/esm/src/index.js",
"types": "./dist/types/src/index.d.ts",
"exports": {
".": {
"types": "./dist/types/src/index.d.ts",
"import": "./dist/esm/src/index.js",
"require": "./dist/cjs/src/index.js"
}
},
"files": [
"dist",
"./README.md",
"./LICENSE"
],
"scripts": {
"lint": "eslint \"**/*.ts{,x}\"",
"build": "tsc -b tsconfig.cjs.json tsconfig.esm.json",
"typecheck": "yarn tsc --noEmit",
"prepublishOnly": "yarn clean && yarn build",
"test": "ts-mocha --type-check --config ../../../.mocharc.js --exit 'test/**/*.spec.ts'",
"clean": "rm -rf dist node_modules *.tsbuildinfo"
},
"dependencies": {
"@pcd/pcd-types": "0.15.0",
"@pcd/pod": "0.5.0"
},
"devDependencies": {
"@pcd/eslint-config-custom": "0.15.0",
"@pcd/tsconfig": "0.15.0",
"@types/chai": "^4.3.5",
"@types/mocha": "^10.0.1",
"@zk-kit/eddsa-poseidon": "^1.1.0",
"eslint": "^8.57.0",
"mocha": "^10.2.0",
"ts-mocha": "^10.0.0",
"typescript": "^5.3.3"
},
"publishConfig": {
"access": "public"
}
}
60 changes: 60 additions & 0 deletions packages/pcd/pod-email-pcd/src/PODEmailPCD.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { PCD, StringArgument } from "@pcd/pcd-types";
import type {
PODEdDSAPublicKeyValue,
PODEntries,
PODStringValue
} from "@pcd/pod";

export const PODEmailPCDTypeName = "pod-email-pcd";

export type PODEmailPCDArgs = {
/**
* The signer's EdDSA private key. This is a 32-byte value used to sign the
* message. See {@link @pcd/pod!decodePrivateKey} in `@pcd/pod` if you need
* to manipulate or convert this value.
*/
privateKey: StringArgument;

/**
* The verified email address
*/
emailAddress: StringArgument;

/**
* The signer's semaphore v4 public key
* @todo link to documentation on public key format
*/
semaphoreV4PublicKey: StringArgument;

/**
* A unique string identifying the PCD
*/
id: StringArgument;
};

export type PODEmailPCDRequiredEntries = {
emailAddress: PODStringValue;
semaphoreV4PublicKey: PODEdDSAPublicKeyValue;
};

export interface PODEmailPCDClaim {
podEntries: PODEntries & PODEmailPCDRequiredEntries;
signerPublicKey: string;
}

export interface PODEmailPCDProof {
signature: string;
}

export class PODEmailPCD implements PCD<PODEmailPCDClaim, PODEmailPCDProof> {
type = PODEmailPCDTypeName;
claim: PODEmailPCDClaim;
proof: PODEmailPCDProof;
id: string;

constructor(id: string, claim: PODEmailPCDClaim, proof: PODEmailPCDProof) {
this.id = id;
this.claim = claim;
this.proof = proof;
}
}
136 changes: 136 additions & 0 deletions packages/pcd/pod-email-pcd/src/PODEmailPCDPackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { DisplayOptions, PCDPackage, SerializedPCD } from "@pcd/pcd-types";
import { POD, PODEntries } from "@pcd/pod";
import {
PODEmailPCD,
PODEmailPCDArgs,
PODEmailPCDClaim,
PODEmailPCDProof,
PODEmailPCDRequiredEntries,
PODEmailPCDTypeName
} from "./PODEmailPCD";

function loadPOD(pcd: PODEmailPCD): POD {
return POD.load(
pcd.claim.podEntries,
pcd.proof.signature,
pcd.claim.signerPublicKey
);
}

export async function prove(args: PODEmailPCDArgs): Promise<PODEmailPCD> {
if (!args.privateKey.value) {
throw new Error("missing private key");
}

if (!args.emailAddress.value) {
throw new Error("missing email value");
}

if (!args.semaphoreV4PublicKey.value) {
throw new Error("missing semaphore v4 public key");
}

const podEntries: PODEmailPCDRequiredEntries = {
emailAddress: {
type: "string",
value: args.emailAddress.value
},
semaphoreV4PublicKey: {
type: "eddsa_pubkey",
value: args.semaphoreV4PublicKey.value
}
};

const pod = POD.sign(podEntries, args.privateKey.value);

const id = args.id.value ?? pod.signature;

return new PODEmailPCD(
id,
{
podEntries,
signerPublicKey: pod.signerPublicKey
},
{
signature: pod.signature
}
);
}

export async function verify(pcd: PODEmailPCD): Promise<boolean> {
return loadPOD(pcd).verifySignature();
}

export async function serialize(
pcd: PODEmailPCD
): Promise<SerializedPCD<PODEmailPCD>> {
return {
type: PODEmailPCDTypeName,
pcd: JSON.stringify({
id: pcd.id,
pod: loadPOD(pcd).toJSON()
})
};
}

function checkPODEntries(
podEntries: PODEntries
): podEntries is PODEntries & PODEmailPCDRequiredEntries {
if (!podEntries.emailAddress) {
throw new Error("emailAddress entry is missing");
}

if (!podEntries.semaphoreV4PublicKey) {
throw new Error("semaphoreV4PublicKey entry is missing");
}

if (typeof podEntries.emailAddress.value !== "string") {
throw new Error("emailAddress entry is not a string");
}

if (podEntries.semaphoreV4PublicKey.type !== "eddsa_pubkey") {
throw new Error("semaphoreV4PublicKey entry is not an eddsa public key");
}

return true;
}

export async function deserialize(serialized: string): Promise<PODEmailPCD> {
const wrapper = JSON.parse(serialized);
const pod = POD.fromJSON(wrapper.pod);
const podEntries = pod.content.asEntries();

if (!checkPODEntries(podEntries)) {
throw new Error("invalid pod entries");
}

return new PODEmailPCD(
wrapper.id,
{
podEntries: podEntries,
signerPublicKey: pod.signerPublicKey
},
{ signature: pod.signature }
);
}

export function getDisplayOptions(pcd: PODEmailPCD): DisplayOptions {
return {
header: "Verified Email",
displayName: pcd.claim.podEntries.emailAddress.value
};
}

export const PODEmailPCDPackage: PCDPackage<
PODEmailPCDClaim,
PODEmailPCDProof,
PODEmailPCDArgs,
undefined
> = {
name: PODEmailPCDTypeName,
getDisplayOptions,
prove,
verify,
serialize,
deserialize
};
3 changes: 3 additions & 0 deletions packages/pcd/pod-email-pcd/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function (): void {
// Package typescript goes here
}
Loading

0 comments on commit dcd8f49

Please sign in to comment.