diff --git a/.env.example b/.env.example index 8c51b53..d3f360d 100644 --- a/.env.example +++ b/.env.example @@ -2,5 +2,5 @@ PCDPASS_URL="http://localhost:3000" # Server. -SERVER_PORT=3000 +SERVER_PORT=3005 PRIVATE_KEY="0x42" diff --git a/README.md b/README.md index 3903a34..73540f3 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,37 @@

-| This repository contains a sample application demonstrating how to utilize the PCD SDK to add a PCD to the PCD passport. | -| ------------------------------------------------------------------------------------------------------------------------ | +| This monorepo contains a sample client and server to demonstrate the PCD SDK usage for issuing a PCD with a PCD passport. | +| ------------------------------------------------------------------------------------------------------------------------- | + +## 🖼 Context + +Proof-Carrying Data (PCD) is a self-contained, independent entity of data carrying sufficient information to be verified and interpreted by a consumer without reliance on exchanges between the producer and consumer. It comprises two primary components: the 'claim,' a straightforward statement (dubbed 'facts'), and the 'proof,' proving the truth of the statement by mathematical or cryptographic means. The elegance lies in the fact that no external information is necessary for validation; everything essential resides within. PCDs have the potential to revolutionize data handling. Envision a world where data flows unrestricted, free from the confinement of silos. PCDs can restore data ownership to individuals, facilitating portability across platforms, and removing barriers. PCD Issuers, whether your bank or Reddit, offer data imbued with cryptography or math (think Merkle Trees and signatures). You can blend it with your private data, creating your unique PCD. Any third-party application (PCD Consumer), from social networks to lending services, can wield this PCD as a key to unlock their services. + +## ▶️ Workflow + +Now, let's embark on a step-by-step journey to harness the potential of Proof-Carrying Data (PCD) utilizing EdDSA PCDs. In this scenario, a PCD comprises a 'claim,' represented as a simple message (the name of your chosen color), and a 'proof' (a signature) crafted by the PCD Issuer's confidential EdDSA key. + +### Step 1: Prepare Your PCD Passport +Before delving into PCDs, ensure you have a valid PCD Passport identity instance. Your PCD Passport can accommodate multiple identities, each potentially carrying several PCDs (akin to attestations). You can efficiently manage these identities using the [zupass](https://github.com/proofcarryingdata/zupass/) PCD Passport. + +### Step 2: Request a PCD from the PCD Issuer +Here's where the magic begins. You'll interact with the PCD Issuer implemented in this repository to get a PCD attesting to a signature on your chosen color name message. The process unfolds as follows: + +1. The PCD Issuer takes your message and, like a digital wizard, applies its private EdDSA key to create a signature. +2. The Issuer returns the serialized PCD, a package of your 'claim' (the color name) and its corresponding 'proof' (the signature). +3. The client, thanks to the PCD SDK, seamlessly adds this PCD to your Passport. + +Now, you've got your 'claim' (the color name) and 'proof' (signature) neatly stored and ready for the next step. + +### Step 3: Interact with the PCD Consumer +With your PCD secured in your Passport, it's time to interact with the PCD Consumer, aptly named [example-consumer](https://github.com/proofcarryingdata/example-consumer). This step allows you to unlock the true potential of your PCD. Here's how it unfolds: + +1. The PCD Consumer client diligently checks if the 'proof' match with the 'claim.' It does this by asking your PCD from your Passport. +2. The client initiates a challenge, verifying the PCD against the public key to check the validity of the EdDSA signature. +3. If the PCD proves its correctness, you gain access to the consumer features. + +Among these features might be the ability to change the background color of the client interface to match the color specified in your original message. ## 🛠 Install @@ -25,18 +54,22 @@ cd && yarn ## 📜 Usage -Copy the `.env.example` file as `.env`: +To run everything, you must have a local instance of the [zupass](https://github.com/proofcarryingdata/zupass/) PCD Passport on your machine. Follow this [guide](https://github.com/proofcarryingdata/zupass/#for-developers-local-development) to get it running correctly. + +Copy the `.env.example` file as `.env` and add your environment variables: ```bash cp .env.example .env ``` -and add your environment variables or run the app in a local network. +Run the following command to build (client + server): -### Local server +```sh +yarn build +``` -You can start your app locally with: +Run the following command to start the application (client + server): ```bash yarn start -``` +``` \ No newline at end of file diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index 9a4b74f..4596847 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -2,14 +2,14 @@ import { useCallback, useState } from "react" import { constructPassportPcdAddRequestUrl, openPassportPopup } from "@pcd/passport-interface" /** - * This component allows users to choose a color, which is then signed by + * This page allows users to choose a color, which is then signed by * the issuer (server) and added to the user's PCDPass as an EdDSA PCD. */ export default function App() { const [color, setColor] = useState("0xffffff") + // Send the color to the server which signs it and returns an EdDSA PCD. const addEdDSAPCD = useCallback(async () => { - // Send the color to the server which signs it and returns an EdDSA PCD. const response = await fetch(`http://localhost:${process.env.SERVER_PORT}/sign-message`, { method: "POST", mode: "cors", @@ -21,6 +21,11 @@ export default function App() { }) }) + if (!response.ok) { + alert("Some error occurred") + return + } + const { serializedPCD } = await response.json() // The EdDSA is added to the user's PCDPass. @@ -37,11 +42,11 @@ export default function App() { <> - + ) } diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 29ef249..0f72861 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -7,7 +7,7 @@ import express, { Express, Request, Response } from "express" dotenv.config({ path: `${process.cwd()}/../../.env` }) if (!process.env.PRIVATE_KEY) { - console.error(`[ERROR] The private key hasn't been set in the environment variables!`) + console.error(`[ERROR] The private key hasn't been set in the environment variables`) process.exit(1) } @@ -27,36 +27,45 @@ app.get("/", (_req: Request, res: Response) => { res.send("Express + TypeScript Server") }) -// It signs a message with the issuer EdDSA private key and returns a serialized PCD. +// Sign a message with the issuer EdDSA private key and returns a serialized PCD. app.post("/sign-message", async (req: Request, res: Response) => { try { if (!req.body.color) { - console.error(`[ERROR] No color specified!`) + console.error(`[ERROR] No color specified`) res.status(400).send() - } else { - console.debug(`[OKAY] color ${process.env.COLOR} has been successfully sent`) - - const pcd = await prove({ - id: { - argumentType: ArgumentTypeName.String - }, - message: { - argumentType: ArgumentTypeName.StringArray, - value: [req.body.color] - }, - privateKey: { - argumentType: ArgumentTypeName.String, - value: process.env.PRIVATE_KEY - } - }) - - const serializedPCD = await serialize(pcd) - - res.json({ serializedPCD }).status(200) + return } + + if (!/^0x[0-9A-F]{6}$/i.test(req.body.color)) { + console.error(`[ERROR] No valid color`) + + res.status(400).send() + return + } + + const pcd = await prove({ + id: { + argumentType: ArgumentTypeName.String + }, + message: { + argumentType: ArgumentTypeName.StringArray, + value: [req.body.color] + }, + privateKey: { + argumentType: ArgumentTypeName.String, + value: process.env.PRIVATE_KEY + } + }) + + console.debug(`[OKAY] color ${req.body.color} has been successfully signed`) + + const serializedPCD = await serialize(pcd) + + res.json({ serializedPCD }).status(200) } catch (error: any) { console.error(`[ERROR] ${error}`) + res.send(500) } })