Skip to content

Commit

Permalink
feat: frontend example complete
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyas-londhe committed Jun 5, 2024
1 parent fbdb142 commit 43754c1
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 99 deletions.
Binary file added .DS_Store
Binary file not shown.
Binary file added circuits/.DS_Store
Binary file not shown.
Binary file added circuits/halo2/.DS_Store
Binary file not shown.
8 changes: 4 additions & 4 deletions circuits/halo2/example-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion circuits/halo2/example-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"axios": "^1.6.8",
"idb": "^8.0.0",
"next": "14.2.2",
"plume-wasm": "^0.1.5",
"plume-wasm": "^0.1.6",
"react": "^18",
"react-dom": "^18"
},
Expand Down
2 changes: 1 addition & 1 deletion circuits/halo2/example-frontend/src/app/nullifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function generateNullifier(): Promise<NullifierData> {
})
)[0];

let message = "CLAIM MY MONI!!";
let message = "CLAIM MY MONIII";

let nullifier = await window.ethereum.request({
method: "eth_getPlumeSignature",
Expand Down
218 changes: 139 additions & 79 deletions circuits/halo2/example-frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { generateNullifier } from "./nullifier";
export default function Home() {
const [isRunning, setIsRunning] = useState(false);
const [worker, setWorker] = useState<Worker | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [selectedOption, setSelectedOption] = useState<string | null>(null);

useEffect(() => {
const initializeWorker = () => {
const newWorker = new Worker(new URL("./worker.ts", import.meta.url), {
type: "module",
});
Expand All @@ -24,71 +25,90 @@ export default function Home() {
}
};
setWorker(newWorker);
};

return () => newWorker.terminate();
useEffect(() => {
initializeWorker();
return () => worker?.terminate();
}, []);

useEffect(() => {
const downloadAndStoreObject = async () => {
try {
const db = await openDB("MyDatabase", 1, {
upgrade(db) {
db.createObjectStore("DataStore");
},
});
if (selectedOption) {
initializeWorker();
const downloadAndStoreObject = async () => {
setIsLoading(true);
try {
const db = await openDB("MyDatabase", 1, {
upgrade(db) {
db.createObjectStore("DataStore");
},
});

const storedVk = await db.get("DataStore", "vk");
const storedPk = await db.get("DataStore", "pk");
const vkKey =
selectedOption === "signature" ? "vk_signature" : "vk_merkle";
const pkKey =
selectedOption === "signature" ? "pk_signature" : "pk_merkle";

if (storedVk && storedPk) {
console.log(
"Verifying key and proving key already stored in Indexeddb",
);
setIsLoading(false);
} else {
console.log("Downloading and storing verifying key and proving key");
const vk = await fetchAndConvertToUint8Array(
"https://storage.googleapis.com/plume-keys/plume_verify_vk_15.bin",
);
const pk = await fetchAndConvertToUint8Array(
"https://storage.googleapis.com/plume-keys/plume_verify_pk_15.bin",
);

await db.put("DataStore", vk, "vk");
await db.put("DataStore", pk, "pk");
const storedVk = await db.get("DataStore", vkKey);
const storedPk = await db.get("DataStore", pkKey);

if (storedVk && storedPk) {
console.log("Keys already stored in Indexeddb");
setIsLoading(false);
} else {
console.log("Downloading and storing keys");
const vkUrl =
selectedOption === "signature"
? "https://storage.googleapis.com/plume-keys/plume_verify_vk_15.bin"
: "https://storage.googleapis.com/plume-keys/plume_merkle_verify_vk_15_8.bin";
const pkUrl =
selectedOption === "signature"
? "https://storage.googleapis.com/plume-keys/plume_verify_pk_15.bin"
: "https://storage.googleapis.com/plume-keys/plume_merkle_verify_pk_15_8.bin";

const vk = await fetchAndConvertToUint8Array(vkUrl);
const pk = await fetchAndConvertToUint8Array(pkUrl);

await db.put("DataStore", vk, vkKey);
await db.put("DataStore", pk, pkKey);
setIsLoading(false);
console.log("Keys downloaded and stored in Indexeddb");
}
} catch (error) {
console.error("Error downloading and storing object:", error);
setIsLoading(false);
console.log(
"Verifying key and proving key downloaded and stored in Indexeddb",
);
}
} catch (error) {
console.error("Error downloading and storing object:", error);
setIsLoading(false);
}
};
};

downloadAndStoreObject();
}, []);
downloadAndStoreObject();
}
}, [selectedOption]);

const runMain = async () => {
if (worker) {
if (worker && selectedOption) {
setIsRunning(true);

try {
const db = await openDB("MyDatabase", 1);
const storedVk = await db.get("DataStore", "vk");
const storedPk = await db.get("DataStore", "pk");
const vkKey =
selectedOption === "signature" ? "vk_signature" : "vk_merkle";
const pkKey =
selectedOption === "signature" ? "pk_signature" : "pk_merkle";

const storedVk = await db.get("DataStore", vkKey);
const storedPk = await db.get("DataStore", pkKey);

if (storedVk && storedPk) {
const plume = await generateNullifier();
const data = {
provingKey: storedPk,
verifyingKey: storedVk,
plume,
option: selectedOption,
};
worker.postMessage({ action: "runMain", data: data });
} else {
console.error("Verifying key and proving key not found in Indexeddb");
console.error("Keys not found in Indexeddb");
setIsRunning(false);
}
} catch (error) {
Expand All @@ -100,48 +120,88 @@ export default function Home() {

return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="max-w-lg w-full px-6 py-8 bg-white rounded-lg shadow-lg">
<div className="max-w-lg w-full px-8 py-10 bg-white rounded-lg shadow-lg">
<h1 className="text-4xl font-bold text-center text-gray-800 mb-8">
PLUME Verification in WASM
</h1>
<div className="flex flex-col items-center">
<button
onClick={runMain}
disabled={isRunning || isLoading}
className={`px-8 py-3 text-lg font-semibold rounded-full focus:outline-none focus:shadow-outline ${
isRunning || isLoading
? "bg-gray-400 cursor-not-allowed"
: "bg-gray-800 hover:bg-gray-700 text-white"
}`}
>
{isRunning ? (
<span className="flex items-center">
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Generating...
<div className="mb-6 flex justify-center space-x-8">
<label
className={`flex items-center justify-center w-48 h-24 cursor-pointer p-4 border-2 rounded-lg ${
selectedOption === "signature"
? "bg-blue-100 border-blue-500"
: "border-gray-300"
}`}
>
<input
type="radio"
value="signature"
checked={selectedOption === "signature"}
onChange={() => setSelectedOption("signature")}
className="form-radio h-0 w-0 opacity-0"
/>
<span className="text-lg text-gray-800 text-center">
Verify PLUME Signature
</span>
</label>
<label
className={`flex items-center justify-center w-48 h-24 cursor-pointer p-4 border-2 rounded-lg ${
selectedOption === "merkle"
? "bg-blue-100 border-blue-500"
: "border-gray-300"
}`}
>
<input
type="radio"
value="merkle"
checked={selectedOption === "merkle"}
onChange={() => setSelectedOption("merkle")}
className="form-radio h-0 w-0 opacity-0"
/>
<span className="text-lg text-gray-800 text-center">
Verify PLUME Signature + Merkle Proof
</span>
) : (
"Generate Nullifier and Proof"
)}
</button>
</label>
</div>
<div className="flex justify-center w-full">
<button
onClick={runMain}
disabled={isRunning || isLoading || !selectedOption}
className={`px-8 py-3 text-lg font-semibold rounded-full focus:outline-none focus:shadow-outline ${
isRunning || isLoading || !selectedOption
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-500 text-white"
}`}
>
{isRunning ? (
<span className="flex items-center">
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Generating...
</span>
) : (
"Generate Nullifier and Proof"
)}
</button>
</div>
{isLoading && (
<p className="mt-4 text-gray-600">
Downloading proving and verifying keys...
Expand Down
39 changes: 39 additions & 0 deletions circuits/halo2/example-frontend/src/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,42 @@ export const getKzgParams = async (k: number) => {
`https://axiom-crypto.s3.amazonaws.com/challenge_0085/kzg_bn254_${k}.srs`,
);
};

export const getMerkleData = (merkleTree: string[][], leafIndex: number) => {
const proof = [];
const proofHelper = [];
let index = leafIndex;

for (let level = 0; level < merkleTree.length - 1; level++) {
const isRightNode = index % 2 === 1;
const siblingIndex = isRightNode ? index - 1 : index + 1;

if (siblingIndex < merkleTree[level].length) {
proof.push(merkleTree[level][siblingIndex]);
proofHelper.push(isRightNode ? "0x0" : "0x1");
}

index = Math.floor(index / 2);
}

const root = merkleTree[merkleTree.length - 1][0];

return { proof, proofHelper, root };
};

export const fetchMerkleData = async (url: string, leafIndex: number) => {
const response = await fetch(url);
const data = await response.json();

let {
proof: merkleProof,
proofHelper,
root,
} = getMerkleData(data, leafIndex);

console.log("proof", merkleProof);
console.log("proofHelper", proofHelper);
console.log("root", root);

return { merkleProof, proofHelper, root };
};
Loading

0 comments on commit 43754c1

Please sign in to comment.