From 910f9c7e0c6c77f4a9930894645c55bb22d42bc7 Mon Sep 17 00:00:00 2001 From: Ali Mahdiyar Date: Sun, 21 Apr 2024 17:24:11 +0330 Subject: [PATCH] (feat) compute result --- packages/nextjs/app/nillion-compute/page.tsx | 277 +++++++++++++------ packages/nextjs/utils/nillion/compute.ts | 4 +- 2 files changed, 198 insertions(+), 83 deletions(-) diff --git a/packages/nextjs/app/nillion-compute/page.tsx b/packages/nextjs/app/nillion-compute/page.tsx index 729eec5..b2d371d 100644 --- a/packages/nextjs/app/nillion-compute/page.tsx +++ b/packages/nextjs/app/nillion-compute/page.tsx @@ -2,17 +2,15 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { prepareWriteContract, waitForTransaction, writeContract } from "@wagmi/core"; +import { prepareWriteContract, readContract, waitForTransaction, writeContract } from "@wagmi/core"; import type { NextPage } from "next"; import { useAccount, useContractRead } from "wagmi"; import { CopyString } from "~~/components/nillion/CopyString"; import { NillionOnboarding } from "~~/components/nillion/NillionOnboarding"; -import RetrieveSecretCommand from "~~/components/nillion/RetrieveSecretCommand"; -import SecretForm from "~~/components/nillion/SecretForm"; import { Address } from "~~/components/scaffold-eth"; import { useDeployedContractInfo } from "~~/hooks/scaffold-eth"; -import { compute } from "~~/utils/nillion/compute"; import { getUserKeyFromSnap } from "~~/utils/nillion/getUserKeyFromSnap"; +import { nillionConfig } from "~~/utils/nillion/nillionConfig"; import { retrieveSecretInteger } from "~~/utils/nillion/retrieveSecretInteger"; import { storeProgram } from "~~/utils/nillion/storeProgram"; import { storeSecretsInteger } from "~~/utils/nillion/storeSecretsInteger"; @@ -31,6 +29,37 @@ const Home: NextPage = () => { args: programId ? [programId] : undefined, watch: true, }); + const secretName = useMemo(() => `r${String(currentRespondersCount)}_response`, [currentRespondersCount]); + const [partyIdToSecretIds, setPartyIdToSecretIds] = useState(""); + useEffect(() => { + async function loadParties() { + if (deployedContractData && currentRespondersCount && programId) { + // const res = (await multicall({ + // allowFailure: false, + // contracts: Array.from(Array(Number(currentRespondersCount)).keys()).map(i => ({ + // address: deployedContractData.address, + // abi: deployedContractData.abi, + // functionName: "partiesAndSecrets", + // args: [programId, BigInt(i)], + // })), + // })) as unknown as string[]; + const res = (await Promise.all( + Array.from(Array(Number(currentRespondersCount)).keys()).map(i => + readContract({ + address: deployedContractData.address, + abi: deployedContractData.abi, + functionName: "partiesAndSecrets", + args: [programId, BigInt(i)], + }), + ), + )) as unknown as string[]; + console.log({ res }); + setPartyIdToSecretIds(res.join(" ")); + } + } + + loadParties(); + }, [currentRespondersCount, deployedContractData, programId]); const { address: connectedAddress } = useAccount(); const [connectedToSnap, setConnectedToSnap] = useState(false); @@ -40,9 +69,20 @@ const Home: NextPage = () => { const [nillionClient, setNillionClient] = useState(null); const [programName] = useState("identicall"); - const [computeResult, setComputeResult] = useState(null); + const [computeResult, setComputeResult] = useState<{ [key: string]: bigint } | null>(null); const [identifier, setIdentifier] = useState(""); const [brightId, setBrightId] = useState(""); + const [secretValue, setSecretValue] = useState(""); + useEffect(() => { + const encoder = new TextEncoder(); + const data = encoder.encode(brightId); + crypto.subtle.digest("SHA-256", data).then(hashBuffer => { + const hashArray = Array.from(new Uint8Array(hashBuffer)); // Convert buffer to byte array + const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, "0")).join(""); // Convert bytes to hex string + setSecretValue(BigInt("0x" + hashHex).toString()); + }); + }, [brightId]); + const [storedSecretsNameToStoreId, setStoredSecretsNameToStoreId] = useState({ my_int1: null, my_int2: null, @@ -58,16 +98,29 @@ const Home: NextPage = () => { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); + + const [myProgramIds, setMyProgramIds] = useState([]); + useEffect(() => { + try { + setMyProgramIds(JSON.parse(localStorage.getItem("myProgramIds") || "[]")); + } catch (e) {} + }, []); // store program in the Nillion network and set the resulting program id const handleStoreProgram = useCallback(async () => { + setStateText("Storing program..."); const newProgramId = await storeProgram(nillionClient, programName); const queryString = new URLSearchParams({ ...Object.fromEntries(searchParams.entries()), p: encodeURIComponent(newProgramId), i: encodeURIComponent(identifier), }); + const newMyProjectIds = [...myProgramIds, newProgramId]; + localStorage.setItem("myProgramIds", JSON.stringify(newMyProjectIds)); + setMyProgramIds(newMyProjectIds); + setStateText(""); router.replace(`${pathname}?${queryString}`); - }, [nillionClient, programName, searchParams, identifier, router, pathname]); + }, [nillionClient, programName, searchParams, identifier, myProgramIds, router, pathname]); + useEffect(() => { const pArg = searchParams.get("p"); if (pArg) { @@ -79,6 +132,8 @@ const Home: NextPage = () => { } }, [searchParams]); + const isMyProgram = useMemo(() => Boolean(programId && myProgramIds.includes(programId)), [myProgramIds, programId]); + async function handleRetrieveInt(secret_name: string, store_id: string | null) { if (store_id) { const value = await retrieveSecretInteger(nillionClient, store_id, secret_name); @@ -123,36 +178,22 @@ const Home: NextPage = () => { const [stateText, setStateText] = useState(""); // handle form submit to store secrets with bindings - async function handleSecretFormSubmit( - secretName: string, - secretValue: string, - permissionedUserIdForRetrieveSecret: string | null, - permissionedUserIdForUpdateSecret: string | null, - permissionedUserIdForDeleteSecret: string | null, - permissionedUserIdForComputeSecret: string | null, - ) { + async function handleSecretFormSubmit() { try { if (programId && deployedContractData && currentRespondersCount !== undefined) { setStateText("Storing secret..."); const partyName = "Responder" + String(currentRespondersCount); - const encoder = new TextEncoder(); - const data = encoder.encode(secretValue); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); // Convert buffer to byte array - const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, "0")).join(""); // Convert bytes to hex string - const secretValueParsed = BigInt("0x" + hashHex).toString(); - const store_id = await storeSecretsInteger( nillion, nillionClient, - [{ name: secretName, value: secretValueParsed }], + [{ name: secretName, value: secretValue }], programId, partyName, - permissionedUserIdForRetrieveSecret ? [permissionedUserIdForRetrieveSecret] : [], - permissionedUserIdForUpdateSecret ? [permissionedUserIdForUpdateSecret] : [], - permissionedUserIdForDeleteSecret ? [permissionedUserIdForDeleteSecret] : [], - permissionedUserIdForComputeSecret ? [permissionedUserIdForComputeSecret] : [], + [], + [], + [], + [programId.split("/")[0]], ); const partyIdToSecretId = `${nillionClient.party_id}:${store_id}`; const { request } = await prepareWriteContract({ @@ -172,7 +213,6 @@ const Home: NextPage = () => { ...prevSecrets, [secretName]: store_id, })); - setStateText(""); } } catch (e) { setStateText(""); @@ -182,17 +222,50 @@ const Home: NextPage = () => { // compute on secrets async function handleCompute() { if (programId) { - await compute( - nillion, - nillionClient, - Object.values(storedSecretsNameToStoreId), - programId, - "same_response_count_for_r4", - ).then(result => setComputeResult(result)); + // await compute( + // nillion, + // nillionClient, + // Object.values(storedSecretsNameToStoreId), + // programId, + // "same_response_count_for_r4", + // ).then(result => setComputeResult(result)); + try { + // create program bindings with the program id + const program_bindings = new nillion.ProgramBindings(programId); + + // add input and output party details (name and party id) to program bindings + Array.from(Array(4).keys()).forEach(i => { + const partyName = "Responder" + i; + const party_id = nillionClient.party_id; + program_bindings.add_input_party(partyName, party_id); + }); + program_bindings.add_input_party("Responder4", nillionClient.party_id); + program_bindings.add_output_party("Responder4", nillionClient.party_id); + + // create a compute time secrets object + const compute_time_secrets = new nillion.Secrets(); + const newComputeTimeSecret = nillion.Secret.new_unsigned_integer(secretValue); + compute_time_secrets.insert("r4_response", newComputeTimeSecret); + + // create a public variables object + const public_variables = new nillion.PublicVariables(); + + // compute + const compute_result_uuid = await nillionClient.compute( + nillionConfig.cluster_id, + program_bindings, + partyIdToSecretIds.split(" ").map(s => s.split(":")[1]), + compute_time_secrets, + public_variables, + ); + setComputeResult(await nillionClient.compute_result(compute_result_uuid)); + } catch (error: any) { + console.log("error", error); + return "error"; + } } } - const secretName = useMemo(() => `r${String(currentRespondersCount)}_response`, [currentRespondersCount]); const [verified, setVerified] = useState(false); return ( @@ -283,69 +356,111 @@ const Home: NextPage = () => { className={`mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm text-black`} /> - + {stateText ? ( + stateText + ) : ( + + )} ) : (
+
+

Investigating person with identifier {identifier}

✅ {programName} program + stored
+ + +
-

Investigating person with identifier {identifier}

+

+ What is the BrightID that you know for the person with identifier {identifier}? +

Current responses count:{" "} {currentRespondersCount !== undefined ? String(currentRespondersCount) : "..."}

-

- Submit the BrightID that you know from this person. The BrightID you enter is hashed before being - sent to the server -

+
The BrightID you enter is hashed before being sent to the server
{!!storedSecretsNameToStoreId[secretName] && userKey ? ( - <> - - - +
Successfully submitted!
) : ( - +
+
+ setBrightId(e.target.value)} + required + disabled={!programId} + className={`mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm ${ + !programId ? "cursor-not-allowed bg-gray-100" : "bg-white" + }`} + /> +
+
+ {stateText ? ( + stateText + ) : isMyProgram ? ( +
+
[DEV Mode] party and secret ids:
+