Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ethereum Group PCD #290

Merged
merged 3 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ Some of these packages are used to share development configuration between the d
- [`@pcd/semaphore-identity-pcd`](packages/semaphore-identity-pcd): a 'self-evident' PCD, representing the public and private components of a Semaphore identity
- [`@pcd/semaphore-signature-pcd`](packages/semaphore-signature-pcd): like `@pcd/semaphore-group-pcd`, but with a more specific purpose of using the semaphore protocol to 'sign' a particular string message on behalf of a particular revealed commitment id.
- [`@pcd/webauthn-pcd`](packages/webauthn-pcd): a pcd that can be used to make claims about [WebAuthn](https://webauthn.guide/) attestations, i.e. signing a particular challenge with a fingerprint, Yubikey, or another [authorization gesture](https://www.w3.org/TR/webauthn-2/#authorization-gesture).
- [`@pcd/ethereum-ownership-pcd`](packages/ethereum-ownership-pcd): a pcd that can be used to claim that a Semaphore identity knows the private key of an ethereum address.
- [`@pcd/ethereum-group-pcd`](packages/ethereum-group-pcd): a pcd that can be used to claim that a Semaphore identity knows the private key of an ethereum address or public key that is part of a merkle group of addresses or public keys, without revealing the specific one.
Comment on lines +127 to +128
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

- ... more to come!

#### shared code packages
Expand Down
1 change: 1 addition & 0 deletions apps/consumer-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"lint": "next lint"
},
"dependencies": {
"@pcd/ethereum-group-pcd": "0.0.1",
"@pcd/ethereum-ownership-pcd": "0.6.0",
"@pcd/passport-interface": "0.6.0",
"@pcd/pcd-types": "0.6.0",
Expand Down
188 changes: 187 additions & 1 deletion apps/consumer-client/pages/examples/add-pcd.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import {
EthereumGroupPCDPackage,
getRawPubKeyBuffer,
GroupType,
} from "@pcd/ethereum-group-pcd";
import { EthereumOwnershipPCDPackage } from "@pcd/ethereum-ownership-pcd";
import {
constructPassportPcdAddRequestUrl,
constructPassportPcdProveAndAddRequestUrl,
openSignedZuzaluSignInPopup,
usePassportPopupMessages,
} from "@pcd/passport-interface";
import { ArgumentTypeName } from "@pcd/pcd-types";
import { ArgumentTypeName, SerializedPCD } from "@pcd/pcd-types";
import { SemaphoreGroupPCDPackage } from "@pcd/semaphore-group-pcd";
import { SemaphoreIdentityPCDPackage } from "@pcd/semaphore-identity-pcd";
import { SemaphoreSignaturePCDPackage } from "@pcd/semaphore-signature-pcd";
import { WebAuthnPCDPackage } from "@pcd/webauthn-pcd";
import { Poseidon, Tree } from "@personaelabs/spartan-ecdsa";
import { Identity } from "@semaphore-protocol/identity";
import { startRegistration } from "@simplewebauthn/browser";
import {
generateRegistrationOptions,
verifyRegistrationResponse,
} from "@simplewebauthn/server";
import { ethers } from "ethers";
import JSONBig from "json-bigint";
import { useEffect, useState } from "react";
import { HomeLink } from "../../components/Core";
import { ExampleContainer } from "../../components/ExamplePage";
import { ZUPASS_URL, ZUZALU_SEMAPHORE_GROUP_URL } from "../../src/constants";
Expand Down Expand Up @@ -62,11 +74,185 @@ export default function Page() {
<button onClick={addWebAuthnPCD}>
add a new webauthn credential to the passport
</button>
<br />
<br />
<AddEthAddrPCDButton />
<br />
<br />
<AddEthGroupPCDButton />
</ExampleContainer>
</div>
);
}

function AddEthAddrPCDButton() {
djma marked this conversation as resolved.
Show resolved Hide resolved
const [pcdStr] = usePassportPopupMessages();
const [isActive, setIsActive] = useState(false);
ichub marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (!pcdStr) return;
if (!isActive) return;

const parsed = JSON.parse(pcdStr) as SerializedPCD;

const ethereum = (window as any).ethereum;
const provider = new ethers.providers.Web3Provider(ethereum);
if (!ethereum) {
alert("Please install MetaMask to use this dApp!");
}

(async function () {
await ethereum.request({ method: "eth_requestAccounts" });
const pcd = await SemaphoreSignaturePCDPackage.deserialize(parsed.pcd);
const signature = await provider
.getSigner()
.signMessage(pcd.claim.identityCommitment);
ichub marked this conversation as resolved.
Show resolved Hide resolved

const popupUrl = window.location.origin + "/popup";

const proofUrl = constructPassportPcdProveAndAddRequestUrl<
typeof EthereumOwnershipPCDPackage
>(ZUPASS_URL, popupUrl, EthereumOwnershipPCDPackage.name, {
identity: {
argumentType: ArgumentTypeName.PCD,
pcdType: SemaphoreIdentityPCDPackage.name,
value: undefined,
userProvided: true,
description:
"The Semaphore Identity which you are proving owns the given Ethereum address.",
},
ethereumAddress: {
argumentType: ArgumentTypeName.String,
value: await provider.getSigner().getAddress(),
},
ethereumSignatureOfCommitment: {
argumentType: ArgumentTypeName.String,
value: signature,
},
});

sendPassportRequest(proofUrl);
})();

setIsActive(false);
}, [pcdStr, isActive]);

return (
<button
onClick={() => {
setIsActive(true);
zupassSignIn("eth-pcd");
}}
>
add a new Ethereum address to the passport
</button>
);
}

async function zupassSignIn(originalSiteName: string) {
openSignedZuzaluSignInPopup(
ZUPASS_URL,
window.location.origin + "/popup",
originalSiteName
);
}

function AddEthGroupPCDButton() {
const [pcdStr] = usePassportPopupMessages();
const [isActive, setIsActive] = useState(false);

useEffect(() => {
if (!pcdStr) return;
if (!isActive) return;

const parsed = JSON.parse(pcdStr) as SerializedPCD;

const ethereum = (window as any).ethereum;
const provider = new ethers.providers.Web3Provider(ethereum);
if (!ethereum) {
alert("Please install MetaMask to use this dApp!");
}

(async function () {
await ethereum.request({ method: "eth_requestAccounts" });
const pcd = await SemaphoreSignaturePCDPackage.deserialize(parsed.pcd);

const msgHash = Buffer.from(
ethers.utils.hashMessage(pcd.claim.identityCommitment).slice(2),
"hex"
);
const signatureOfIdentityCommitment = await provider
.getSigner()
.signMessage(pcd.claim.identityCommitment);

const poseidon = new Poseidon();
await poseidon.initWasm();
const treeDepth = 20; // Provided circuits have tree depth = 20
const pubKeyTree = new Tree(treeDepth, poseidon);

// Add some public keys to the tree
for (const member of [
"0x04b4d5188949bf70c4db5e965a9ea67b80407e8ee7fa3a260ccf86e9c0395fe82cba155fdff55829b3c862322aba402d00b563861b603879ee8ae211c34257d4ad",
"0x042d21e6aa2021a991a82d08591fa0528d0bebe4ac9a34d851a74507327d930dec217380bd602fe48a143bb21106ab274d6a51aff396f0e4f7e1e3a8a673d46d83",
]) {
pubKeyTree.insert(poseidon.hashPubKey(getRawPubKeyBuffer(member)));
}
// Add the prover's public key to the tree
const proverPubkeyBuffer: Buffer = getRawPubKeyBuffer(
ethers.utils.recoverPublicKey(msgHash, signatureOfIdentityCommitment)
);
pubKeyTree.insert(poseidon.hashPubKey(proverPubkeyBuffer));
const pubKeyIndex = pubKeyTree.indexOf(
poseidon.hashPubKey(proverPubkeyBuffer)
); // == 2 in this test

// Prove membership of the prover's public key in the tree
const merkleProof = pubKeyTree.createProof(pubKeyIndex);

const popupUrl = window.location.origin + "/popup";
const proofUrl = constructPassportPcdProveAndAddRequestUrl<
typeof EthereumGroupPCDPackage
>(ZUPASS_URL, popupUrl, EthereumGroupPCDPackage.name, {
identity: {
argumentType: ArgumentTypeName.PCD,
pcdType: SemaphoreIdentityPCDPackage.name,
value: undefined,
userProvided: true,
description:
"The Semaphore Identity which you are signing the message.",
},
groupType: {
argumentType: ArgumentTypeName.String,
value: GroupType.PUBLICKEY,
},
signatureOfIdentityCommitment: {
argumentType: ArgumentTypeName.String,
value: signatureOfIdentityCommitment,
},
merkleProof: {
argumentType: ArgumentTypeName.String,
value: JSONBig({ useNativeBigInt: true }).stringify(merkleProof),
ichub marked this conversation as resolved.
Show resolved Hide resolved
},
});

sendPassportRequest(proofUrl);
})();

setIsActive(false);
}, [pcdStr, isActive]);

return (
<button
onClick={() => {
setIsActive(true);
zupassSignIn("eth-group-pcd");
}}
>
add a new Ethereum Group Membership to the passport
</button>
);
}

async function addGroupMembershipProofPCD() {
const url = constructPassportPcdProveAndAddRequestUrl<
typeof SemaphoreGroupPCDPackage
Expand Down
22 changes: 22 additions & 0 deletions apps/passport-client/src/pcdPackages.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should make an equivalent update to apps/passport-server/src/services/provingService.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in startProvingService? what about all the other pcds?

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EthereumGroupPCDPackage } from "@pcd/ethereum-group-pcd";
import { EthereumOwnershipPCDPackage } from "@pcd/ethereum-ownership-pcd";
import { HaLoNoncePCDPackage } from "@pcd/halo-nonce-pcd";
import { PCDPackage } from "@pcd/pcd-types";
Expand Down Expand Up @@ -41,6 +42,26 @@ async function loadPackages(): Promise<PCDPackage[]> {
zkeyFilePath: "/semaphore-artifacts/16.zkey",
});

await EthereumGroupPCDPackage.init({
wasmFilePath: "/semaphore-artifacts/16.wasm",
zkeyFilePath: "/semaphore-artifacts/16.zkey",

// TODO: update these to point to pcd pass' static server
ichub marked this conversation as resolved.
Show resolved Hide resolved
addrMembershipConfig: {
circuit:
"https://storage.googleapis.com/personae-proving-keys/membership/addr_membership.circuit",
witnessGenWasm:
"https://storage.googleapis.com/personae-proving-keys/membership/addr_membership.wasm",
},

pubkeyMembershipConfig: {
circuit:
"https://storage.googleapis.com/personae-proving-keys/membership/pubkey_membership.circuit",
witnessGenWasm:
"https://storage.googleapis.com/personae-proving-keys/membership/pubkey_membership.wasm",
},
});

await RLNPCDPackage.init({
wasmFilePath: SERVER_STATIC_URL + "rln-artifacts/16.wasm",
zkeyFilePath: SERVER_STATIC_URL + "rln-artifacts/16.zkey",
Expand All @@ -55,6 +76,7 @@ async function loadPackages(): Promise<PCDPackage[]> {
SemaphoreIdentityPCDPackage,
SemaphoreSignaturePCDPackage,
EthereumOwnershipPCDPackage,
EthereumGroupPCDPackage,
JubJubSignaturePCDPackage,
RLNPCDPackage,
WebAuthnPCDPackage,
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions packages/ethereum-group-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/ethereum-group-pcd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.js
*.d.ts
*.ts.map
!.eslintrc.js
dist
7 changes: 7 additions & 0 deletions packages/ethereum-group-pcd/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# @pcd/ethereum-group-pcd

## 0.0.1

### Patch Changes

- Hello world
17 changes: 17 additions & 0 deletions packages/ethereum-group-pcd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# `@pcd/ethereum-group-pcd`
ichub marked this conversation as resolved.
Show resolved Hide resolved

PCD which proves that a particular Semaphore Identity owns a particular ethereum address that is part of an address merkle set or public key merkle set, without revealing the ethereum address.

It uses an optimized group ecdsa zk [circuit](https://github.com/personaelabs/spartan-ecdsa) written by Personae Labs.

By pre-computing a set of Ethereum addresses that satisfy a particular property, one can prove that property about themselves without revealing their exact address. For example, proving that one:

- owns a NounDao (punk, or etc) NFT at a particular block
- made a transaction before 2020
- made at least 100 transactions
- owns at least 10 eth
- used tornado cash when it was cool
- has been validated by proof-of-humanity
- participated in a conference that issued a POAP

If you always use one address, proving multiple properties about yourself will narrow the anonymity set. However, if you use multiple addresses, you can own multiple PCDs without narrowing the anonymity set.
Loading