Skip to content

Commit

Permalink
Integrate remote proof generation SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
wryonik committed Aug 27, 2024
1 parent aa16174 commit 0cff0cc
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 240 deletions.
6 changes: 3 additions & 3 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"dependencies": {
"@proof-of-twitter/circuits": "workspace:^",
"@proof-of-twitter/contracts": "workspace:^",
"@rainbow-me/rainbowkit": "^1.3.3",
"@rainbow-me/rainbowkit": "^2.1.4",
"@react-oauth/google": "^0.12.1",
"@tanstack/react-query": "^5.52.0",
"@zk-email/helpers": "^6.1.3",
"@zk-email/zk-email-sdk": "^1.0.3",
"js-base64": "^3.7.7",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-is": "^18.2.0",
"react-router-dom": "^6.2.2",
"react-use": "^17.3.2",
Expand Down
53 changes: 33 additions & 20 deletions packages/app/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { WagmiConfig, createConfig } from "wagmi";
import { WagmiConfig, WagmiProvider, createConfig } from "wagmi";
import { createPublicClient, http } from "viem";
import { configureChains } from "wagmi";
import { publicProvider } from "wagmi/providers/public";
Expand All @@ -10,42 +10,55 @@ import {
getDefaultWallets,
RainbowKitProvider,
darkTheme,
getDefaultConfig,
} from "@rainbow-me/rainbowkit";
import "@rainbow-me/rainbowkit/styles.css";
import { GoogleOAuthProvider } from "@react-oauth/google";
import { GoogleAuthProvider } from "./contexts/GoogleAuth";
import { ZkEmailSDKProvider } from "@zk-email/zk-email-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const { connectors } = getDefaultWallets({
// const { connectors } = getDefaultWallets({
// appName: "ZK Email - Twitter Verifier",
// chains: [sepolia],
// projectId: "b68298f4e6597f970ac06be1aea7998d",
// });

// const config = createConfig({
// autoConnect: true,
// publicClient: createPublicClient({
// chain: sepolia,
// transport: http(),
// }),
// connectors: connectors,
// });

const config = getDefaultConfig({
appName: "ZK Email - Twitter Verifier",
chains: [sepolia],
projectId: "b68298f4e6597f970ac06be1aea7998d",
chains: [sepolia],
// transports: http(),
});

const config = createConfig({
autoConnect: true,
publicClient: createPublicClient({
chain: sepolia,
transport: http(),
}),
connectors: connectors,
});
const queryClient = new QueryClient();

ReactDOM.render(
<React.StrictMode>
<ZkEmailSDKProvider
clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}
zkEmailSDKRegistryUrl="https://registry-dev.zkregex.com"
>
<WagmiConfig config={config}>
<RainbowKitProvider chains={[sepolia]} theme={darkTheme()}>
<GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
<GoogleAuthProvider>
<App />
</GoogleAuthProvider>
</GoogleOAuthProvider>
</RainbowKitProvider>
</WagmiConfig>{" "}
<GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider theme={darkTheme()}>
<GoogleAuthProvider>
<App />
</GoogleAuthProvider>
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>{" "}
</GoogleOAuthProvider>
</ZkEmailSDKProvider>
</React.StrictMode>,
document.getElementById("root")
Expand Down
146 changes: 128 additions & 18 deletions packages/app/src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,45 @@ import { formatDateTime } from "../helpers/dateTimeFormat";
import EmailInputMethod from "../components/EmailInputMethod";
import { randomUUID } from "crypto";
import { useZkEmailSDK } from "@zk-email/zk-email-sdk";
import { calculateSignalLength, circuitOutputToArgs } from "../utils";
import { Hex } from "viem";

const CIRCUIT_NAME = "twitter";
const entry = {
id: "clyhcz1tl0001r4i0t87dk48g",
title: "Proof of Twitter V2",
slug: "zk-email/proof-of-twitter-v2",
description:
"Use a password reset email to proof you own the email connected to a twitter handle.",
createdAt: "2024-07-11T14:22:41.146Z",
updatedAt: "2024-08-16T08:10:32.501Z",
createdBy: "zk-email",
tags: ["twitter", "identity", "email"],
status: "COMPLETED",
parameters: {
name: "twitterProof",
values: [
{
name: "handle",
parts: [
{ is_public: false, regex_def: "email was meant for @" },
{ is_public: true, regex_def: "[a-zA-Z0-9_]+" },
],
location: "body",
maxLength: 64,
},
],
version: "v2",
senderDomain: "x.com",
externalInputs: [{ name: "address", maxLength: 64 }],
emailBodyMaxLength: 2816,
ignoreBodyHashCheck: false,
shaPrecomputeSelector: ">Not my account<",
},
emailQuery: "Password reset request from: [email protected] ",
verifierContractAddress: "0xdb862d400104ba05590b9b657e3fb2e80e202c15",
contractAddress: "0x53acd2f8d26f7bc292852aadd8484531a3117157",
};

export const MainPage: React.FC<{}> = (props) => {
const { address } = useAccount();
Expand Down Expand Up @@ -98,6 +135,11 @@ export const MainPage: React.FC<{}> = (props) => {
const [externalInputs, setExternalInputs] = useState<Record<string, string>>(
{}
);
const [signalLength, setSignalLength] = useState<number>(1);
const [
isRemoteProofVerificationLoading,
setIsRemoteProofVerificationLoading,
] = useState<boolean>(false);

const [stopwatch, setStopwatch] = useState<Record<string, number>>({
startedDownloading: 0,
Expand Down Expand Up @@ -150,22 +192,6 @@ export const MainPage: React.FC<{}> = (props) => {
].flat();
};

const { config } = usePrepareContractWrite({
// @ts-ignore
address: import.meta.env.VITE_CONTRACT_ADDRESS,
abi: abi,
functionName: "mint",
args: [
reformatProofForChain(proof),
publicSignals ? JSON.parse(publicSignals) : [],
],
enabled: !!(proof && publicSignals),
onError: (error: { message: any }) => {
console.error(error.message);
// TODO: handle errors
},
});

const { data, isPending, isSuccess, writeContract } = useWriteContract();

const handleFetchEmails = async () => {
Expand Down Expand Up @@ -207,6 +233,8 @@ export const MainPage: React.FC<{}> = (props) => {
if (!inputWorkers["zk-email/proof-of-twitter-v2"]) {
setAreInputWorkerCreating(true);
createInputWorker("zk-email/proof-of-twitter-v2");
setSignalLength(calculateSignalLength(entry));

const entryExternalInputs = [
{
name: "address",
Expand Down Expand Up @@ -322,7 +350,64 @@ export const MainPage: React.FC<{}> = (props) => {
}
}, [inputWorkers]);

console.log(inputWorkers);
console.log(
proofStatus[Object.keys(proofStatus)[0]]?.proof,
proofStatus[Object.keys(proofStatus)[0]],
proofStatus
);

const verifyRemoteProof = async (id: string) => {
if (!proofStatus[Object.keys(proofStatus)[0]]) {
return;
}
setIsRemoteProofVerificationLoading(true);
await writeContract(
{
abi: [
{
inputs: [
{
internalType: "uint256[2]",
name: "a",
type: "uint256[2]",
},
{
internalType: "uint256[2][2]",
name: "b",
type: "uint256[2][2]",
},
{
internalType: "uint256[2]",
name: "c",
type: "uint256[2]",
},
{
internalType: `uint256[${signalLength}]`,
name: "signals",
type: `uint256[${signalLength}]`,
},
],
name: "verify",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const,
address: entry.contractAddress! as Hex,
functionName: "verify",
args: circuitOutputToArgs({
proof: proofStatus[Object.keys(proofStatus)[0]].proof,
public: proofStatus[Object.keys(proofStatus)[0]].publicOutput,
}) as any,
},
{
onError: (error, variables, context) =>
setIsRemoteProofVerificationLoading(false),
onSuccess: (data, variables, context) =>
setIsRemoteProofVerificationLoading(false),
}
);
};

return (
<Container>
Expand Down Expand Up @@ -497,6 +582,10 @@ export const MainPage: React.FC<{}> = (props) => {
value={ethereumAddress}
onChange={(e) => {
setEthereumAddress(e.currentTarget.value);
setExternalInputs({
...externalInputs,
address: e.target.value,
});
}}
/>
<Button
Expand Down Expand Up @@ -660,7 +749,9 @@ export const MainPage: React.FC<{}> = (props) => {
Verify
</Button>
<Button
disabled={!verificationPassed || isPending || isSuccess || !writeContract}
disabled={
!verificationPassed || isPending || isSuccess || !writeContract
}
onClick={async () => {
setStatus("sending-on-chain");
writeContract({
Expand All @@ -685,6 +776,25 @@ export const MainPage: React.FC<{}> = (props) => {
? "Mint Twitter badge on-chain"
: "Verify first, before minting on-chain!"}
</Button>
<Button
disabled={!proofStatus[Object.keys(proofStatus)[0]]}
onClick={async () => {
try {
verifyRemoteProof(entry.id);
} catch (er: any) {
setVerificationMessage("Failed to verify " + er.toString());
setVerificationPassed(false);
}
}}
>
Verify Remote proof
{isRemoteProofVerificationLoading ? (
<div className="loader" style={{ marginLeft: "1rem" }} />
) : (
""
)}
</Button>

{isSuccess && (
<div>
Transaction:{" "}
Expand Down
55 changes: 55 additions & 0 deletions packages/app/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { toHex } from "viem";

type Proof = {
proof: {
pi_a: string[2];
pi_b: string[2][2];
pi_c: string[2];
};
public: string[];
};

export function calculateSignalLength(entry: any) {
let startIdx = 1;
const parameters = entry.parameters as {
values: { maxLength: number }[];
externalInputs: { maxLength: number }[];
emailBodyMaxLength: number;
enableMasking: boolean;
};
if (parameters.enableMasking) {
startIdx += parameters.emailBodyMaxLength;
}
if (!parameters.externalInputs) {
parameters.externalInputs = [];
}
const valuesLength = parameters.values.reduce(
(acc, value) =>
acc + Math.floor(value.maxLength / 31) + (value.maxLength % 31 ? 1 : 0),
startIdx
);
const inputsLength = parameters.externalInputs.reduce(
(acc, value) =>
acc + Math.floor(value.maxLength / 31) + (value.maxLength % 31 ? 1 : 0),
0
);
return valuesLength + inputsLength;
}

export const circuitOutputToArgs = (output: Proof) => {
return [
[toHex(BigInt(output.proof.pi_a[0])), toHex(BigInt(output.proof.pi_a[1]))],
[
[
toHex(BigInt(output.proof.pi_b[0][1])),
toHex(BigInt(output.proof.pi_b[0][0])),
],
[
toHex(BigInt(output.proof.pi_b[1][1])),
toHex(BigInt(output.proof.pi_b[1][0])),
],
],
[toHex(BigInt(output.proof.pi_c[0])), toHex(BigInt(output.proof.pi_c[1]))],
output.public.map((x) => toHex(BigInt(x))),
];
};
Loading

0 comments on commit 0cff0cc

Please sign in to comment.