From ed925cd498df564162ba40b7ff02137869e415d8 Mon Sep 17 00:00:00 2001 From: lukachi Date: Tue, 26 Mar 2024 18:26:59 +0200 Subject: [PATCH] define response for app requests wip --- src/api/modules/verify/consts/index.ts | 23 ---- src/api/modules/verify/enums/index.ts | 3 +- src/api/modules/verify/helpers/zkp.ts | 116 +++++++----------- src/api/modules/verify/index.ts | 1 - src/api/modules/verify/types/zkp.ts | 78 ++++++++---- src/pages/Votings/hooks/app-request.ts | 39 ++---- src/pages/Votings/hooks/app-voting-details.ts | 6 +- .../components/VotingAlive/index.tsx | 27 ++-- .../components/VotingRegistration/index.tsx | 96 +++++++-------- 9 files changed, 177 insertions(+), 212 deletions(-) delete mode 100644 src/api/modules/verify/consts/index.ts diff --git a/src/api/modules/verify/consts/index.ts b/src/api/modules/verify/consts/index.ts deleted file mode 100644 index 8936857..0000000 --- a/src/api/modules/verify/consts/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ClaimTypes } from '@/api/modules/verify/enums' - -export const CLAIM_TYPES_CHECKS_VALUES_MAP: Record = { - [ClaimTypes.AuthClaim]: 'hash', -} - -export const CLAIM_TYPES_MAP_ON_CHAIN: Record = { - [ClaimTypes.AuthClaim]: { - id: 1, - circuitId: 'credentialAtomicQueryMTPV2OnChain', - query: { - allowedIssuers: ['*'], - context: - 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld', - credentialSubject: { - birthday: { - $eq: CLAIM_TYPES_CHECKS_VALUES_MAP[ClaimTypes.AuthClaim], - }, - }, - type: ClaimTypes.AuthClaim, - }, - }, -} diff --git a/src/api/modules/verify/enums/index.ts b/src/api/modules/verify/enums/index.ts index bdacb49..b31c9a8 100644 --- a/src/api/modules/verify/enums/index.ts +++ b/src/api/modules/verify/enums/index.ts @@ -1,5 +1,6 @@ export enum ClaimTypes { - AuthClaim = 'AuthClaim', + Registration = 'registration', + Voting = 'voting', } export enum VotingTypes { diff --git a/src/api/modules/verify/helpers/zkp.ts b/src/api/modules/verify/helpers/zkp.ts index 02bc1f9..8659939 100644 --- a/src/api/modules/verify/helpers/zkp.ts +++ b/src/api/modules/verify/helpers/zkp.ts @@ -1,14 +1,9 @@ +import { config } from '@config' import { Poseidon } from '@iden3/js-crypto' import { BytesLike, utils } from 'ethers' import { groth16 } from 'snarkjs' -import { v4 as uuidv4 } from 'uuid' - -import { - AppRequestOpts, - CLAIM_TYPES_MAP_ON_CHAIN, - ProofRequestResponse, -} from '@/api/modules/verify' -import { config } from '@/config' + +import { ClaimTypes, ClaimTypesMapOnChain, ProofRequestResponse } from '@/api/modules/verify' import { VerifierHelper } from '@/types/contracts/Voting' export type SecretPair = { @@ -112,30 +107,7 @@ function swap(arr: unknown[], i: number, j: number) { arr[j] = temp } -export const createRequestOnChain = ( - reason: string, - message: string, - sender: string, - callbackUrl: string, -) => { - const uuid = uuidv4() - - return { - id: uuid, - thid: uuid, - from: sender, - typ: 'application/iden3comm-plain-json', - type: 'https://iden3-communication.io/authorization/1.0/request', - body: { - reason: reason, - message: message, - callbackUrl: callbackUrl, - scope: [], - }, - } -} - -export const createRequest = async (opts: AppRequestOpts) => { +export const buildAppRequest = async (opts: ClaimTypesMapOnChain[T]) => { // const { data } = await api.get<{ // verification_id: string // jwt: string @@ -146,31 +118,20 @@ export const createRequest = async (opts: AppRequestOpts) => { jwt: 'qwerty', } - const request = createRequestOnChain( - opts.reason, - opts.message, - opts.sender, - `${config.API_URL}/integrations/verify-proxy/v1/public/verify/callback/${data.verification_id}`, - ) - return { + verificationId: data.verification_id, request: { - ...request, - id: data.verification_id, - thid: data.verification_id, - body: { - ...request.body, - scope: [CLAIM_TYPES_MAP_ON_CHAIN[opts.claimType]], - }, + ...opts, + callbackUrl: `${config.API_URL}/integrations/verify-proxy/v1/public/verify/callback/${data.verification_id}`, }, jwtToken: data.jwt, } } -export const getRequestResponse = ( +export const subscribeToAppRequestResponse = ( verificationId: string, jwtToken: string, - callback: (res: ProofRequestResponse) => void, + callback: (res: ProofRequestResponse[T]) => void, ): (() => void) => { // Create WebSocket connection. // const socket = new WebSocket( @@ -199,31 +160,46 @@ export const getRequestResponse = ( // // return socket.close - // TODO: remove mocked data - setTimeout(() => { - callback({ - // eslint-disable-next-line max-len - jwz: `eyJhbGciOiJncm90aDE2IiwiY2lyY3VpdElkIjoiYXV0aFYyIiwiY3JpdCI6WyJjaXJjdWl0SWQiXSwidHlwIjoiSldaIn0.bXltZXNzYWdl.eyJwcm9vZiI6eyJwaV9hIjpbIjU4OTA4MTc2NDc0NDY4MzQ2MDU3NjU3NzA0NTExMDIyMDg4NjMyMDkxNDgwMTE5NDgzNjA0MDQ3NDU0ODA2NzE1NDM2MjU5MTkwNDIiLCI2OTY1MzI0OTI3MDYzMDQxOTU2NTIwODg5ODU1MDcxNjU1OTg5Mzg4NzQyODM1ODgzOTI1NjU4MDI1NDE0MjM4OTQ2OTkxNjE1ODMwIiwiMSJdLCJwaV9iIjpbWyIxNjgwMjkyNTc5OTM3NjI4MDExOTc1MTk2MTk1MDEzNjQ5NjkyMjMyOTU1NDI5Mjc0Nzc5OTE1NDI2MDQwMzMwNTM0Njc1NDU1Mzk5NCIsIjIwNzkzNDcyNDAwMzczNDkzMjIyNzAyNDY4NDcxMjQzNzcwMzk3NzY1MzY0OTc3NDA0NDQwNTQ2Mzc0MTkxNjU2OTM0NDE3Mjg1MDQxIl0sWyI2MTI1MjcxNjYyOTI4NDUzMjQ5NDgyMjc5MjQ2ODA2NTIxNTE2MzU5NDQwMTcxMDM1MzgxMzU4OTI3MjI4Njc2NTQxNTc0NTg5MDkxIiwiNTY4MDc3OTcxNTc0MjMyMjI0ODQyOTM0NDc1ODA5NDk0MzMyMzE1OTIzOTQzNjkyNzI3MjM3NDEwOTkxMzYzOTAyMjM2NDMyMjYwNiJdLFsiMSIsIjAiXV0sInBpX2MiOlsiOTQ4MTkzNTE5MTMwNTA0OTM5MTA3MjkxMDkxNzE2ODQzNzA0OTI4MjQyMzc3NDQ5MDM4NzMwNDU3NzM3MTI4Mjc0Mjc1NTc3ODYwOCIsIjEyMDMxNDE1NjE1ODExNTEzNzc2OTcwMDYwOTgzMDk2NTMxNzcwMTcwNDAxMjkzODEwMTQwMDY2ODM1NzkyMjk4NTcwNDQxMzcyMTg3IiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjIzMTQ4OTM2NDY2MzM0MzUwNzQ0NTQ4NzkwMDEyMjk0NDg5MzY1MjA3NDQwNzU0NTA5OTg4OTg2Njg0Nzk3NzA4MzcwMDUxMDczIiwiNjExMDUxNzc2ODI0OTU1OTIzODE5MzQ3NzQzNTQ1NDc5MjAyNDczMjE3Mzg2NTQ4ODkwMDI3MDg0OTYyNDMyODY1MDc2NTY5MTQ5NCIsIjEyNDM5MDQ3MTE0Mjk5NjE4NTg3NzQyMjA2NDc2MTA3MjQyNzM3OTg5MTg0NTc5OTE0ODYwMzE1NjcyNDQxMDA3NjcyNTkyMzk3NDciXX0`, - statesMerkleData: { - issuerId: 'mockedIssuerId', - state: { - hash: '', - createdAtTimestamp: '1234123', - index: '', - lastUpdateOperationIndex: '', - }, - merkleProof: [], + // TODO: get data from callback url and parse jwz token + // if (!proofResponse?.jwz) { + // throw new Error('Invalid proof data') + // } + // + // const jwzToken = await Token.parse(proofResponse?.jwz) + // + // const zkProofPayload = JSON.parse(jwzToken.getPayload()) + // + // const zkpProof = zkProofPayload.body.scope[0] as ZKProof + + const mockedData: ProofRequestResponse[ClaimTypes.Registration] = { + type: ClaimTypes.Registration, + data: { + proveIdentityParams: { + issuingAuthority: '123', + documentNullifier: '123', + commitment: '0x123', }, - updateStateDetails: { - stateRootHash: '', - gistRootDataStruct: { - root: '', - createdAtTimestamp: '', + registerProofParams: { + a: ['123', '123'], + b: [ + ['123', '123'], + ['123', '123'], + ], + c: ['123', '123'], + inputs: ['123', '123', '123'], + statesMerkleData: { + merkleProof: ['0x123'], + createdAtTimestamp: '123', + issuerState: '123', + issuerId: '123', }, - proof: '', }, - documentNullifier: '', - }) + }, + } + + // TODO: remove mocked data + setTimeout(() => { + callback(mockedData as ProofRequestResponse[T]) }, 5_000) return () => {} diff --git a/src/api/modules/verify/index.ts b/src/api/modules/verify/index.ts index 8e4800d..b6c869a 100644 --- a/src/api/modules/verify/index.ts +++ b/src/api/modules/verify/index.ts @@ -1,4 +1,3 @@ -export * from './consts' export * from './enums' export * from './helpers' export * from './types' diff --git a/src/api/modules/verify/types/zkp.ts b/src/api/modules/verify/types/zkp.ts index a8149c0..1d71448 100644 --- a/src/api/modules/verify/types/zkp.ts +++ b/src/api/modules/verify/types/zkp.ts @@ -1,31 +1,65 @@ -import { ClaimTypes } from '@/api/modules/verify' +import { ClaimTypes } from '@/api/modules/verify/enums' export type ProofRequestResponse = { - updateStateDetails: { - stateRootHash: string - gistRootDataStruct: { - root: string | number - createdAtTimestamp: string | number + [ClaimTypes.Registration]: { + type: ClaimTypes.Registration + data: { + proveIdentityParams: { + issuingAuthority: string + documentNullifier: string + commitment: string + } + registerProofParams: { + a: string[] + b: [[string, string], [string, string]] + c: [string, string] + inputs: string[] + statesMerkleData: { + merkleProof: string[] + createdAtTimestamp: string + issuerState: string + issuerId: string + } + } } - proof: string } - jwz: string - documentNullifier: string - statesMerkleData?: { - issuerId: string - state: { - index: string - hash: string - createdAtTimestamp: string - lastUpdateOperationIndex: string + [ClaimTypes.Voting]: { + type: ClaimTypes.Voting + data: { + proveIdentityParams: { + issuingAuthority: string + documentNullifier: string + commitment: string + } + registerProofParams: { + a: string[] + b: [[string, string], [string, string]] + c: [string, string] + inputs: string[] + statesMerkleData: { + merkleProof: string + createdAtTimestamp: string + issuerState: string + issuerId: string + } + } } - merkleProof: string[] } } -export type AppRequestOpts = { - claimType: ClaimTypes - reason: string - message: string - sender: string +export type ClaimTypesMapOnChain = { + [ClaimTypes.Registration]: { + type: ClaimTypes.Registration + data: { + metadata_url: string + // callback: string + } + } + [ClaimTypes.Voting]: { + type: ClaimTypes.Voting + data: { + metadata_url: string + // callback: string + } + } } diff --git a/src/pages/Votings/hooks/app-request.ts b/src/pages/Votings/hooks/app-request.ts index f9cfb8e..d4ddc6a 100644 --- a/src/pages/Votings/hooks/app-request.ts +++ b/src/pages/Votings/hooks/app-request.ts @@ -1,46 +1,33 @@ import { useCallback, useState } from 'react' import { - AppRequestOpts, - createRequest, - generateSecrets, - getCommitment, - getRequestResponse, + buildAppRequest, + ClaimTypes, + ClaimTypesMapOnChain, ProofRequestResponse, - SecretPair, + subscribeToAppRequestResponse, } from '@/api/modules/verify' -export const useAppRequest = ( - reqOpts: AppRequestOpts, +export const useAppRequest = ( + reqOpts: ClaimTypesMapOnChain[T], ): { request: string cancelSubscription: () => void - start: ( - onSuccess: (proofResponse: ProofRequestResponse, secrets: SecretPair) => Promise, - ) => Promise + start: (onSuccess: (response: ProofRequestResponse[T]) => Promise) => Promise } => { const [request, setRequest] = useState('') const [cancelSubscription, setCancelSubscription] = useState<() => void>(() => {}) const start = useCallback( - async ( - onSuccess: (proofResponse: ProofRequestResponse, secrets: SecretPair) => Promise, - ) => { - // TODO: get secrets from app? - const secrets = generateSecrets() + async (onSuccess: (response: ProofRequestResponse[T]) => Promise) => { + const { request, verificationId, jwtToken } = await buildAppRequest(reqOpts) - // TODO: ? - const commitment = getCommitment(secrets) - - // TODO: upd params - const { request, jwtToken } = await createRequest(reqOpts) - - const cancelSubscription = getRequestResponse( - request.id, + const cancelSubscription = subscribeToAppRequestResponse( + verificationId, jwtToken, - (res: ProofRequestResponse) => { - onSuccess(res, secrets) + response => { + onSuccess(response as ProofRequestResponse[T]) }, ) diff --git a/src/pages/Votings/hooks/app-voting-details.ts b/src/pages/Votings/hooks/app-voting-details.ts index 1d7f081..e6be91c 100644 --- a/src/pages/Votings/hooks/app-voting-details.ts +++ b/src/pages/Votings/hooks/app-voting-details.ts @@ -99,13 +99,11 @@ export const useAppVotingDetails = (pairIdOrInstance: string | AppVoting) => { ]) const getIsUserRegistered = useCallback( - async (proofResponse: ProofRequestResponse) => { + async (documentNullifier: string) => { if (!provider?.rawProvider) throw new TypeError('Provider is not connected') if (!appVoting) throw new TypeError('Voting is not found') - if (!proofResponse?.documentNullifier) return false - const registrationInstance = VotingRegistration__factory.connect( appVoting.registration.contract_address, provider.rawProvider as unknown as providers.JsonRpcProvider, @@ -118,7 +116,7 @@ export const useAppVotingDetails = (pairIdOrInstance: string | AppVoting) => { provider.rawProvider as unknown as providers.JsonRpcProvider, ) - return registerVerifierInstance.isIdentityRegistered(proofResponse.documentNullifier) + return registerVerifierInstance.isIdentityRegistered(documentNullifier) }, [appVoting, provider?.rawProvider], ) diff --git a/src/pages/Votings/pages/VotingsId/components/VotingAlive/index.tsx b/src/pages/Votings/pages/VotingsId/components/VotingAlive/index.tsx index 21884db..53b8693 100644 --- a/src/pages/Votings/pages/VotingsId/components/VotingAlive/index.tsx +++ b/src/pages/Votings/pages/VotingsId/components/VotingAlive/index.tsx @@ -5,11 +5,9 @@ import { useCallback, useState } from 'react' import { AppVoting, ClaimTypes, - getCommitment, getVoteZKP, poseidonHash, ProofRequestResponse, - SecretPair, vote, } from '@/api/modules/verify' import { NoDataViewer } from '@/common' @@ -35,17 +33,18 @@ export default function VotingAlive({ appVoting, ...rest }: Props) { const { getIsUserRegistered } = useAppVotingDetails(appVoting) - const { request, start, cancelSubscription } = useAppRequest({ - claimType: ClaimTypes.AuthClaim, - reason: '', // FIXME: use real data - message: '', // FIXME: use real data - sender: '', // FIXME: use real data + const { request, start, cancelSubscription } = useAppRequest({ + type: ClaimTypes.Voting, + data: { + metadata_url: appVoting.registration.remark, + // callbackUrl will be auto appended + }, }) const [selectedCandidateHash, setSelectedCandidateHash] = useState('') const voteForCandidate = useCallback( - async (proofResponse: ProofRequestResponse, secrets: SecretPair, candidateHash: string) => { + async (proofResponse: ProofRequestResponse[ClaimTypes.Voting], candidateHash: string) => { setIsPending(true) try { @@ -54,7 +53,9 @@ export default function VotingAlive({ appVoting, ...rest }: Props) { if (!provider?.rawProvider) throw new TypeError('Provider is not connected') - if (!(await getIsUserRegistered(proofResponse))) { + if ( + !(await getIsUserRegistered(proofResponse.data.proveIdentityParams.documentNullifier)) + ) { throw new Error('User is not registered') // TODO: add notification } @@ -63,9 +64,7 @@ export default function VotingAlive({ appVoting, ...rest }: Props) { provider.rawProvider as unknown as providers.JsonRpcProvider, ) - const commitment = getCommitment(secrets) - - const commitmentIndex = poseidonHash(commitment) + const commitmentIndex = poseidonHash(proofResponse.data.proveIdentityParams.commitment) const root = await registrationInstance.getRoot() @@ -96,8 +95,8 @@ export default function VotingAlive({ appVoting, ...rest }: Props) { const handleVote = useCallback( async (candidateHash: string) => { - await start(async (proofResponse, secrets) => { - await voteForCandidate(proofResponse, secrets, candidateHash) + await start(async proofResponse => { + await voteForCandidate(proofResponse, candidateHash) setIsAppRequestModalShown(false) }) diff --git a/src/pages/Votings/pages/VotingsId/components/VotingRegistration/index.tsx b/src/pages/Votings/pages/VotingsId/components/VotingRegistration/index.tsx index 0c15151..9c94ac8 100644 --- a/src/pages/Votings/pages/VotingsId/components/VotingRegistration/index.tsx +++ b/src/pages/Votings/pages/VotingsId/components/VotingRegistration/index.tsx @@ -1,15 +1,7 @@ -import { Token, ZKProof } from '@iden3/js-jwz' import { Alert, Button, Paper, Stack, StackProps, Typography, useTheme } from '@mui/material' import { useCallback, useState } from 'react' -import { - AppVoting, - ClaimTypes, - getCommitment, - ProofRequestResponse, - SecretPair, - signUpForVoting, -} from '@/api/modules/verify' +import { AppVoting, ClaimTypes, ProofRequestResponse, signUpForVoting } from '@/api/modules/verify' import { BusEvents } from '@/enums' import { bus, ErrorHandler, sleep } from '@/helpers' import { useAppRequest, useAppVotingDetails } from '@/pages/Votings/hooks' @@ -32,61 +24,61 @@ export default function VotingRegistration({ appVoting, ...rest }: Props) { const { getIsUserRegistered } = useAppVotingDetails(appVoting) - const { request, start, cancelSubscription } = useAppRequest({ - claimType: ClaimTypes.AuthClaim, - reason: '', // FIXME: use real data - message: '', // FIXME: use real data - sender: '', // FIXME: use real data + const { request, start, cancelSubscription } = useAppRequest({ + type: ClaimTypes.Registration, + data: { + metadata_url: appVoting.registration.remark, + // callbackUrl will be auto appended + }, }) const buildTxAndSignUpForVoting = useCallback( - async (proofResponse: ProofRequestResponse, secrets: SecretPair) => { + async (proofResponse: ProofRequestResponse[ClaimTypes.Registration]) => { setIsPending(true) try { - if (!proofResponse?.jwz) { - throw new Error('Invalid proof data') - } - - const jwzToken = await Token.parse(proofResponse?.jwz) - - const zkProofPayload = JSON.parse(jwzToken.getPayload()) - - const zkpProof = zkProofPayload.body.scope[0] as ZKProof - - if (!proofResponse?.statesMerkleData || !proofResponse?.updateStateDetails || !zkpProof) { - throw new Error('Invalid proof data') - } - const proveIdentityParams: IBaseVerifier.ProveIdentityParamsStruct = { statesMerkleData: { - issuerId: proofResponse?.statesMerkleData.issuerId, - issuerState: proofResponse?.statesMerkleData.state.hash, - createdAtTimestamp: proofResponse?.statesMerkleData.state.createdAtTimestamp, - merkleProof: proofResponse?.statesMerkleData.merkleProof, + issuerId: proofResponse.data.registerProofParams.statesMerkleData.issuerId, + issuerState: proofResponse.data.registerProofParams.statesMerkleData.issuerState, + createdAtTimestamp: + proofResponse.data.registerProofParams.statesMerkleData.createdAtTimestamp, + merkleProof: proofResponse.data.registerProofParams.statesMerkleData.merkleProof, }, - inputs: zkpProof.pub_signals.map?.(el => BigInt(el)), - a: [zkpProof?.proof.pi_a[0], zkpProof?.proof.pi_a[1]], + inputs: proofResponse.data.registerProofParams.inputs.map?.(el => BigInt(el)), + a: [ + proofResponse.data.registerProofParams?.a[0], + proofResponse.data.registerProofParams.a[1], + ], b: [ - [zkpProof?.proof.pi_b[0][1], zkpProof?.proof.pi_b[0][0]], - [zkpProof?.proof.pi_b[1][1], zkpProof?.proof.pi_b[1][0]], + [ + proofResponse.data.registerProofParams.b[0][1], + proofResponse.data.registerProofParams.b[0][0], + ], + [ + proofResponse.data.registerProofParams.b[1][1], + proofResponse.data.registerProofParams.b[1][0], + ], + ], + c: [ + proofResponse.data.registerProofParams.c[0], + proofResponse.data.registerProofParams.c[1], ], - c: [zkpProof?.proof.pi_c[0], zkpProof?.proof.pi_c[1]], } const registerProofParams: IRegisterVerifier.RegisterProofParamsStruct = { - issuingAuthority: '13281866', // FIXME - documentNullifier: proofResponse?.documentNullifier, + issuingAuthority: proofResponse.data.proveIdentityParams.issuingAuthority, + documentNullifier: proofResponse.data.proveIdentityParams.documentNullifier, // TODO: handle 2 cases, when user signed before vote starts, and after - commitment: getCommitment(secrets), + commitment: proofResponse.data.proveIdentityParams.commitment, } const transitStateParams: IBaseVerifier.TransitStateParamsStruct = { - newIdentitiesStatesRoot: proofResponse?.updateStateDetails.stateRootHash, - gistData: { - root: proofResponse?.updateStateDetails.gistRootDataStruct.root, - createdAtTimestamp: - proofResponse?.updateStateDetails.gistRootDataStruct.createdAtTimestamp, - }, - proof: proofResponse?.updateStateDetails.proof, + // newIdentitiesStatesRoot: proofResponse?.updateStateDetails.stateRootHash, + // gistData: { + // root: proofResponse?.updateStateDetails.gistRootDataStruct.root, + // createdAtTimestamp: + // proofResponse?.updateStateDetails.gistRootDataStruct.createdAtTimestamp, + // }, + // proof: proofResponse?.updateStateDetails.proof, } const contractInterface = VotingRegistration__factory.createInterface() @@ -115,10 +107,12 @@ export default function VotingRegistration({ appVoting, ...rest }: Props) { ) const signUp = useCallback(async () => { - await start(async (proofResponse, secrets) => { - await buildTxAndSignUpForVoting(proofResponse, secrets) + await start(async proofResponse => { + await buildTxAndSignUpForVoting(proofResponse) - setIsUserRegistered(await getIsUserRegistered(proofResponse)) + setIsUserRegistered( + await getIsUserRegistered(proofResponse.data.proveIdentityParams.documentNullifier), + ) setIsAppRequestModalShown(false) })