From fc346fd6bdd9ad38a76257e0e846660a5b33d717 Mon Sep 17 00:00:00 2001 From: vmidyllic <74898029+vmidyllic@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:41:31 +0200 Subject: [PATCH 01/10] fix bug with backup status issuer for Iden3SparseMerkleTreeProof (#154) * fix bug with backup status issuer for Iden3SparseMerkleTreeProof * make a call through resolver --- package-lock.json | 4 ++-- package.json | 2 +- src/credentials/status/reverse-sparse-merkle-tree.ts | 9 ++++++--- tests/rhs/rhs.test.ts | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fe1bdfb..140304cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.4.1", + "version": "1.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@0xpolygonid/js-sdk", - "version": "1.4.1", + "version": "1.4.2", "license": "AGPL-3.0", "dependencies": { "ajv": "8.12.0", diff --git a/package.json b/package.json index ef7185bd..7c21fd05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.4.1", + "version": "1.4.2", "description": "SDK to work with Polygon ID", "main": "dist/node/cjs/index.js", "module": "dist/node/esm/index.js", diff --git a/src/credentials/status/reverse-sparse-merkle-tree.ts b/src/credentials/status/reverse-sparse-merkle-tree.ts index d544b25a..83313e9c 100644 --- a/src/credentials/status/reverse-sparse-merkle-tree.ts +++ b/src/credentials/status/reverse-sparse-merkle-tree.ts @@ -6,6 +6,7 @@ import { CredentialStatusResolver, CredentialStatusResolveOptions } from './reso import { CredentialStatus, RevocationStatus, State } from '../../verifiable'; import { VerifiableConstants, CredentialStatusType } from '../../verifiable/constants'; import { isGenesisState } from './utils'; +import { IssuerResolver } from './sparse-merkle-tree'; /** * ProofNode is a partial Reverse Hash Service result @@ -122,12 +123,14 @@ export class RHSResolver implements CredentialStatusResolver { } catch (e: unknown) { if (credentialStatus?.statusIssuer?.type === CredentialStatusType.SparseMerkleTreeProof) { try { - return await (await fetch(credentialStatus.id)).json(); + return await new IssuerResolver().resolve(credentialStatus.statusIssuer); } catch (e) { - throw new Error(`can't fetch revocation status from backup endpoint`); + throw new Error( + `can't fetch revocation status from backup endpoint: ${(e as Error)?.message}` + ); } } - throw new Error(`can't fetch revocation status`); + throw new Error(`can't fetch revocation status: ${(e as Error)?.message}`); } } diff --git a/tests/rhs/rhs.test.ts b/tests/rhs/rhs.test.ts index ed30989d..277041d6 100644 --- a/tests/rhs/rhs.test.ts +++ b/tests/rhs/rhs.test.ts @@ -194,7 +194,7 @@ describe('rhs', () => { await credWallet.save(issuerAuthCredential); const credBasicStatus: CredentialStatus = { - id: 'issuerurl', + id: 'http://issuerurl.com', revocationNonce: 0, type: CredentialStatusType.SparseMerkleTreeProof }; @@ -249,7 +249,7 @@ describe('rhs', () => { }); const credBasicStatus: CredentialStatus = { - id: 'issuerurl', + id: 'http://issuerurl.coml', revocationNonce: 0, type: CredentialStatusType.SparseMerkleTreeProof }; @@ -326,7 +326,7 @@ describe('rhs', () => { expect(credential).not.to.be.undefined; const credBasicStatus: CredentialStatus = { - id: 'issuerurl', + id: 'http://issuerurl.coml', revocationNonce: 0, type: CredentialStatusType.SparseMerkleTreeProof }; From 5169b9a458927a1cb6b1125656d1a88958005ade Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 4 Dec 2023 12:36:59 +0200 Subject: [PATCH 02/10] rm POC linked wrappers --- src/circuits/index.ts | 1 - src/circuits/linked-multi-query.ts | 135 ------------------ src/circuits/linked-nullifier.ts | 89 ------------ .../data/linked-nullifier-inputs.json | 19 --- tests/circuits/linked-nullifier.test.ts | 56 -------- 5 files changed, 300 deletions(-) delete mode 100644 src/circuits/linked-multi-query.ts delete mode 100644 src/circuits/linked-nullifier.ts delete mode 100644 tests/circuits/data/linked-nullifier-inputs.json delete mode 100644 tests/circuits/linked-nullifier.test.ts diff --git a/src/circuits/index.ts b/src/circuits/index.ts index c23d59a7..7e02f20b 100644 --- a/src/circuits/index.ts +++ b/src/circuits/index.ts @@ -9,4 +9,3 @@ export * from './atomic-query-v3-on-chain'; export * from './atomic-query-sig-v2-on-chain'; export * from './auth-v2'; export * from './state-transition'; -export * from './linked-nullifier'; diff --git a/src/circuits/linked-multi-query.ts b/src/circuits/linked-multi-query.ts deleted file mode 100644 index f9b2e4c8..00000000 --- a/src/circuits/linked-multi-query.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Claim } from '@iden3/js-iden3-core'; -import { byteDecoder, byteEncoder } from '../utils'; -import { - BaseConfig, - bigIntArrayToStringArray, - existenceToInt, - getNodeAuxValue, - prepareCircuitArrayValues, - prepareSiblingsStr -} from './common'; -import { Query, ValueProof } from './models'; - -/** - * LinkedMultiQuery circuit representation - * Inputs and public signals declaration, marshalling and parsing - * - * @beta - * @class LinkedMultiQueryInputs - */ -export class LinkedMultiQueryInputs extends BaseConfig { - linkID!: bigint; - linkNonce!: bigint; - claim!: Claim; - query!: Query[]; - - // InputsMarshal returns Circom private inputs for linkedMultiQueryInputs.circom - inputsMarshal(): Uint8Array { - const valueProofs: ValueProof[] = this.query.map((i) => i.valueProof); - - const s: Partial = { - linkID: this.linkID.toString(), - linkNonce: this.linkNonce.toString(), - issuerClaim: this.claim.marshalJson(), - claimSchema: this.claim.getSchemaHash().bigInt().toString(), - claimPathNotExists: valueProofs.map((i) => existenceToInt(i.mtp.existence)), - claimPathMtp: valueProofs.map((i) => prepareSiblingsStr(i.mtp, this.getMTLevelsClaim())) - }; - - const nodAuxJSONLDs = valueProofs.map((i) => getNodeAuxValue(i.mtp)); - - s.claimPathMtpNoAux = nodAuxJSONLDs.map((i) => i.noAux); - s.claimPathMtpAuxHi = nodAuxJSONLDs.map((i) => i.key.bigInt().toString()); - s.claimPathMtpAuxHv = nodAuxJSONLDs.map((i) => i.value.bigInt().toString()); - - s.claimPathKey = valueProofs.map((i) => i.path.toString()); - - s.claimPathValue = valueProofs.map((i) => i.value.toString()); - - s.slotIndex = this.query.map((i) => i.slotIndex); - - s.operator = this.query.map((i) => i.operator); - - const valuesArr = this.query.map((i) => - prepareCircuitArrayValues(i.values, this.getValueArrSize()) - ); - s.value = valuesArr.map((i) => bigIntArrayToStringArray(i)); - - return byteEncoder.encode(JSON.stringify(s)); - } -} - -/** - * @beta - */ -interface LinkedMultiQueryCircuitInputs { - linkID: string; - linkNonce: string; - issuerClaim: string[]; - claimSchema: string; - claimPathNotExists: number[]; - claimPathMtp: string[][]; - claimPathMtpNoAux: string[]; - claimPathMtpAuxHi: string[]; - claimPathMtpAuxHv: string[]; - claimPathKey: string[]; - claimPathValue: string[]; - slotIndex: number[]; - operator: number[]; - value: string[][]; -} - -// LinkedMultiQueryPubSignals linkedMultiQuery.circom public signals -/** - * public signals - * - * @beta - * @class LinkedMultiQueryPubSignals - */ -export class LinkedMultiQueryPubSignals { - merklized!: number; - operatorOutput!: bigint[]; - circuitQueryHash!: bigint[]; - linkID!: bigint; - - /** - * PubSignalsUnmarshal unmarshal linkedMultiQuery.circom public inputs to LinkedMultiQueryPubSignals - * - * @beta - * @param {Uint8Array} data - * @returns LinkedMultiQueryPubSignals - */ - pubSignalsUnmarshal(data: Uint8Array, queryLength: number): LinkedMultiQueryPubSignals { - const len = queryLength * 2 + 2; - const sVals: string[] = JSON.parse(byteDecoder.decode(data)); - - if (sVals.length !== len) { - throw new Error(`invalid number of Output values expected ${len} got ${sVals.length}`); - } - - let fieldIdx = 0; - - // -- merklized - this.merklized = parseInt(sVals[fieldIdx]); - fieldIdx++; - - // - operatorOutput - this.operatorOutput = []; - for (let i = 0; i < queryLength; i++) { - this.operatorOutput.push(BigInt(sVals[fieldIdx])); - fieldIdx++; - } - - // - circuitQueryHash - this.circuitQueryHash = []; - for (let i = 0; i < queryLength; i++) { - this.circuitQueryHash.push(BigInt(sVals[fieldIdx])); - fieldIdx++; - } - - // -- linkID - this.linkID = BigInt(sVals[fieldIdx]); - - return this; - } -} diff --git a/src/circuits/linked-nullifier.ts b/src/circuits/linked-nullifier.ts deleted file mode 100644 index ba9c9034..00000000 --- a/src/circuits/linked-nullifier.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Claim, Id } from '@iden3/js-iden3-core'; -import { byteDecoder, byteEncoder } from '../utils'; - -/** - * LinkedNullifier circuit representation - * Inputs and public signals declaration, marshalling and parsing - * - * @beta - * @class LinkedNullifierInputs - */ -export class LinkedNullifierInputs { - linkID!: bigint; - linkNonce!: bigint; - issuerClaim!: Claim; - id!: Id; - claimSubjectProfileNonce!: bigint; - verifierID!: Id; - verifierSessionID!: bigint; - - // InputsMarshal returns Circom private inputs for nullifier.circom - inputsMarshal(): Uint8Array { - const s: LinkedNullifierCircuitInputs = { - linkID: this.linkID.toString(), - linkNonce: this.linkNonce.toString(), - issuerClaim: this.issuerClaim.marshalJson(), - userGenesisID: this.id.bigInt().toString(), - claimSubjectProfileNonce: this.claimSubjectProfileNonce.toString(), - claimSchema: this.issuerClaim.getSchemaHash().bigInt().toString(), - verifierID: this.verifierID.bigInt().toString(), - verifierSessionID: this.verifierSessionID.toString() - }; - - return byteEncoder.encode(JSON.stringify(s)); - } -} - -/** - * @beta - */ -interface LinkedNullifierCircuitInputs { - linkID: string; - linkNonce: string; - issuerClaim: string[]; - userGenesisID: string; - claimSubjectProfileNonce: string; - claimSchema: string; - verifierID: string; - verifierSessionID: string; -} - -// LinkedNullifierPubSignals nullifier.circom public signals -/** - * public signals - * - * @beta - * @class LinkedNullifierPubSignals - */ -export class LinkedNullifierPubSignals { - nullifier!: bigint; - linkID!: bigint; - verifierID!: Id; - verifierSessionID!: bigint; - - /** - * PubSignalsUnmarshal unmarshal nullifier.circom public inputs to LinkedNullifierPubSignals - * - * @beta - * @param {Uint8Array} data - * @returns LinkedNullifierPubSignals - */ - pubSignalsUnmarshal(data: Uint8Array): LinkedNullifierPubSignals { - const len = 4; - const sVals: string[] = JSON.parse(byteDecoder.decode(data)); - - if (sVals.length !== len) { - throw new Error(`invalid number of Output values expected ${len} got ${sVals.length}`); - } - - this.nullifier = BigInt(sVals[0]); - - this.linkID = BigInt(sVals[1]); - - this.verifierID = Id.fromBigInt(BigInt(sVals[2])); - - this.verifierSessionID = BigInt(sVals[3]); - - return this; - } -} diff --git a/tests/circuits/data/linked-nullifier-inputs.json b/tests/circuits/data/linked-nullifier-inputs.json deleted file mode 100644 index 1bb901ab..00000000 --- a/tests/circuits/data/linked-nullifier-inputs.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "linkID": "12341231236643", - "linkNonce": "35346346369657418", - "issuerClaim": [ - "3583233690122716044519380227940806650830", - "26109404700696283154998654512117952420503675471097392618762221546565140481", - "10", - "0", - "30803922965249841627828060161", - "0", - "0", - "0" - ], - "userGenesisID": "26109404700696283154998654512117952420503675471097392618762221546565140481", - "claimSubjectProfileNonce": "21313111", - "claimSchema": "180410020913331409885634153623124536270", - "verifierID": "21929109382993718606847853573861987353620810345503358891473103689157378049", - "verifierSessionID": "322215" -} diff --git a/tests/circuits/linked-nullifier.test.ts b/tests/circuits/linked-nullifier.test.ts deleted file mode 100644 index 64a4d762..00000000 --- a/tests/circuits/linked-nullifier.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Id } from '@iden3/js-iden3-core'; -import { LinkedNullifierInputs, LinkedNullifierPubSignals } from '../../src/circuits'; -import { IdentityTest, defaultUserClaim, userPK } from './utils'; -import expectedJson from './data/linked-nullifier-inputs.json'; -import { expect } from 'chai'; -import { byteDecoder, byteEncoder } from '../../src'; - -describe('linked-nullifier', () => { - it('TestLinkedNullifierInputs_InputsMarshal', async () => { - const user = await IdentityTest.newIdentity(userPK); - const id = user.id; - const claim = defaultUserClaim(id); - - const inputs = new LinkedNullifierInputs(); - inputs.linkID = BigInt('12341231236643'); - inputs.linkNonce = BigInt('35346346369657418'); - inputs.issuerClaim = claim; - inputs.id = user.id; - inputs.claimSubjectProfileNonce = BigInt('21313111'); - inputs.verifierID = Id.fromBigInt( - BigInt('21929109382993718606847853573861987353620810345503358891473103689157378049') - ); - inputs.verifierSessionID = BigInt(322215); - - const bytesInputs = inputs.inputsMarshal(); - - const actualJson = JSON.parse(byteDecoder.decode(bytesInputs)); - expect(actualJson).to.deep.equal(expectedJson); - }); - - it('LinkedNullifierPubSignals_CircuitUnmarshal', () => { - // generate mock Data. - const nullifier = BigInt(1233342); - const linkID = BigInt(565429123812); - const verifierID = Id.fromBigInt( - BigInt('21929109382993718606847853573861987353620810345503358891473103689157378049') - ); - const verifierSessionID = BigInt(2033444042); - - const out = byteEncoder.encode( - JSON.stringify([ - nullifier.toString(), - linkID.toString(), - verifierID.bigInt().toString(), - verifierSessionID.toString() - ]) - ); - - const ao = new LinkedNullifierPubSignals(); - ao.pubSignalsUnmarshal(out); - expect(nullifier.toString()).to.deep.equal(ao.nullifier.toString()); - expect(linkID.toString()).to.deep.equal(ao.linkID.toString()); - expect(verifierID.string()).to.deep.equal(ao.verifierID.string()); - expect(verifierSessionID.toString()).to.deep.equal(ao.verifierSessionID.toString()); - }); -}); From 433e37b450fd3cd3a225447bdddb05dcaaefc864 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 4 Dec 2023 12:47:11 +0200 Subject: [PATCH 03/10] update version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 140304cd..aa15fe0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.4.2", + "version": "1.4.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@0xpolygonid/js-sdk", - "version": "1.4.2", + "version": "1.4.3", "license": "AGPL-3.0", "dependencies": { "ajv": "8.12.0", diff --git a/package.json b/package.json index 7c21fd05..ce91fe95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.4.2", + "version": "1.4.3", "description": "SDK to work with Polygon ID", "main": "dist/node/cjs/index.js", "module": "dist/node/esm/index.js", From 01397a1c555256c6f8fd7e2d3cca49d5655160eb Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 4 Dec 2023 18:41:27 +0200 Subject: [PATCH 04/10] refactor ProofType duplicate & add generateQueryV3 --- src/circuits/atomic-query-v3-on-chain.ts | 13 ++-- src/circuits/atomic-query-v3.ts | 16 ++--- src/proof/proof-service.ts | 63 +++++++++++++++++++ .../circuits/atomic-query-v3-on-chain.test.ts | 7 +-- tests/circuits/atomic-query-v3.test.ts | 7 +-- tests/proofs/mtp.test.ts | 25 ++++++-- tests/proofs/sig.test.ts | 22 ++++++- 7 files changed, 120 insertions(+), 33 deletions(-) diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index 23dd0b4d..bde6f81b 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -10,8 +10,9 @@ import { import { CircuitError, GISTProof, Query, TreeState, ValueProof } from './models'; import { Hash, Proof, ZERO_HASH } from '@iden3/js-merkletree'; import { byteDecoder, byteEncoder } from '../utils'; -import { AtomicProofType, ClaimWithSigAndMTPProof } from './atomic-query-v3'; +import { ClaimWithSigAndMTPProof } from './atomic-query-v3'; import { Signature } from '@iden3/js-crypto'; +import { ProofType } from '../verifiable'; const zero = '0'; @@ -41,7 +42,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { query!: Query; currentTimeStamp!: number; - proofType!: AtomicProofType; + proofType!: ProofType; linkNonce!: bigint; verifierID!: Id; verifierSessionID!: bigint; @@ -86,7 +87,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { } } - if (this.proofType === AtomicProofType.BJJSignature2021) { + if (this.proofType === ProofType.BJJSignature) { if (!this.claim.signatureProof?.issuerAuthIncProof.proof) { throw new Error(CircuitError.EmptyIssuerAuthClaimProof); } @@ -99,7 +100,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { throw new Error(CircuitError.EmptyClaimSignature); } } - if (this.proofType === AtomicProofType.Iden3SparseMerkleTreeProof) { + if (this.proofType === ProofType.Iden3SparseMerkleTreeProof) { if (!this.claim?.incProof?.proof) { throw new Error(CircuitError.EmptyClaimProof); } @@ -243,7 +244,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { s.isRevocationChecked = 0; } - if (this.proofType === AtomicProofType.BJJSignature2021) { + if (this.proofType === ProofType.BJJSignature) { s.proofType = '1'; s.issuerClaimSignatureR8x = this.claim.signatureProof?.signature.R8[0].toString(); @@ -278,7 +279,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { .toString(); this.fillMTPProofsWithZero(s); - } else if (this.proofType === AtomicProofType.Iden3SparseMerkleTreeProof) { + } else if (this.proofType === ProofType.Iden3SparseMerkleTreeProof) { s.proofType = '2'; const incProofTreeState = this.claim.incProof?.treeState; diff --git a/src/circuits/atomic-query-v3.ts b/src/circuits/atomic-query-v3.ts index 0b9a86b2..a46f539c 100644 --- a/src/circuits/atomic-query-v3.ts +++ b/src/circuits/atomic-query-v3.ts @@ -10,11 +10,7 @@ import { import { BJJSignatureProof, CircuitError, MTProof, Query, ValueProof } from './models'; import { Hash, Proof, ZERO_HASH } from '@iden3/js-merkletree'; import { byteDecoder, byteEncoder } from '../utils'; - -export enum AtomicProofType { - BJJSignature2021 = 'BJJSignature2021', - Iden3SparseMerkleTreeProof = 'Iden3SparseMerkleTreeProof' -} +import { ProofType } from '../verifiable'; const zero = '0'; @@ -41,7 +37,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { skipClaimRevocationCheck!: boolean; query!: Query; currentTimeStamp!: number; - proofType!: AtomicProofType; + proofType!: ProofType; linkNonce!: bigint; verifierID!: Id; verifierSessionID!: bigint; @@ -63,7 +59,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { throw new Error(CircuitError.InvalidProofType); } - if (this.proofType === AtomicProofType.BJJSignature2021) { + if (this.proofType === ProofType.BJJSignature) { if (!this.claim.signatureProof?.issuerAuthIncProof.proof) { throw new Error(CircuitError.EmptyIssuerAuthClaimProof); } @@ -76,7 +72,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { throw new Error(CircuitError.EmptyClaimSignature); } } - if (this.proofType === AtomicProofType.Iden3SparseMerkleTreeProof) { + if (this.proofType === ProofType.Iden3SparseMerkleTreeProof) { if (!this.claim?.incProof?.proof) { throw new Error(CircuitError.EmptyClaimProof); } @@ -165,7 +161,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { s.isRevocationChecked = 0; } - if (this.proofType === AtomicProofType.BJJSignature2021) { + if (this.proofType === ProofType.BJJSignature) { s.proofType = '1'; s.issuerClaimSignatureR8x = this.claim.signatureProof?.signature.R8[0].toString(); @@ -200,7 +196,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { .toString(); this.fillMTPProofsWithZero(s); - } else if (this.proofType === AtomicProofType.Iden3SparseMerkleTreeProof) { + } else if (this.proofType === ProofType.Iden3SparseMerkleTreeProof) { s.proofType = '2'; const incProofTreeState = this.claim.incProof?.treeState; diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index e2ebe706..30a7f454 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -12,6 +12,7 @@ import { AtomicQueryMTPV2OnChainInputs, AtomicQuerySigV2Inputs, AtomicQuerySigV2OnChainInputs, + AtomicQueryV3Inputs, AuthV2Inputs, CircuitClaim, CircuitId, @@ -344,6 +345,9 @@ export class ProofService implements IProofService { case CircuitId.AtomicQuerySigV2OnChain: generateInputFn = this.generateQuerySigV2OnChainInputs.bind(this); break; + case CircuitId.AtomicQueryV3: + generateInputFn = this.generateQueryV3Inputs.bind(this); + break; default: throw new Error(`circuit with id ${proofReq.circuitId} is not supported by issuer`); } @@ -563,6 +567,65 @@ export class ProofService implements IProofService { return { inputs: circuitInputs.inputsMarshal(), vp }; } + private async generateQueryV3Inputs( + preparedCredential: PreparedCredential, + identifier: DID, + proofReq: ZeroKnowledgeProofRequest, + params: InputsParams + ): Promise<{ inputs: Uint8Array; vp?: object }> { + const circuitClaimData = await this.newCircuitClaimData( + preparedCredential.credential, + preparedCredential.credentialCoreClaim + ); + + circuitClaimData.nonRevProof = toClaimNonRevStatus(preparedCredential.revStatus); + let proofType: ProofType; + switch (proofReq.query.proofType) { + case ProofType.BJJSignature: + proofType = ProofType.BJJSignature; + break; + case ProofType.Iden3SparseMerkleTreeProof: + proofType = ProofType.Iden3SparseMerkleTreeProof; + break; + default: + if (circuitClaimData.proof) { + proofType = ProofType.Iden3SparseMerkleTreeProof; + } else if (circuitClaimData.signatureProof) { + proofType = ProofType.BJJSignature; + } else { + throw Error('claim has no MTP or signature proof'); + } + break; + } + + const circuitInputs = new AtomicQueryV3Inputs(); + circuitInputs.id = DID.idFromDID(identifier); + circuitInputs.claim = { + issuerID: circuitClaimData?.issuerId, + signatureProof: circuitClaimData.signatureProof, + claim: circuitClaimData.claim, + nonRevProof: circuitClaimData.nonRevProof, + incProof: { proof: circuitClaimData.proof, treeState: circuitClaimData.treeState } + }; + circuitInputs.requestID = BigInt(proofReq.id); + circuitInputs.claimSubjectProfileNonce = BigInt(params.credentialSubjectProfileNonce); + circuitInputs.profileNonce = BigInt(params.authProfileNonce); + circuitInputs.skipClaimRevocationCheck = params.skipRevocation; + const { query, vp } = await this.toCircuitsQuery( + proofReq.query, + preparedCredential.credential, + preparedCredential.credentialCoreClaim + ); + circuitInputs.query = query; + circuitInputs.currentTimeStamp = getUnixTimestamp(new Date()); + + circuitInputs.proofType = proofType; + circuitInputs.linkNonce = BigInt(0); + circuitInputs.verifierID = DID.idFromDID(identifier); + circuitInputs.verifierSessionID = BigInt(0); + return { inputs: circuitInputs.inputsMarshal(), vp }; + } + // NewCircuitClaimData generates circuits claim structure private async newCircuitClaimData( credential: W3CCredential, diff --git a/tests/circuits/atomic-query-v3-on-chain.test.ts b/tests/circuits/atomic-query-v3-on-chain.test.ts index 4f06ebb6..5a5c8c56 100644 --- a/tests/circuits/atomic-query-v3-on-chain.test.ts +++ b/tests/circuits/atomic-query-v3-on-chain.test.ts @@ -1,7 +1,6 @@ import { Id } from '@iden3/js-iden3-core'; import { Hash } from '@iden3/js-merkletree'; import { - AtomicProofType, AtomicQueryV3OnChainInputs, AtomicQueryV3OnChainPubSignals, Operators, @@ -22,7 +21,7 @@ import { import expectedMtpJson from './data/atomic-query-v3-mtp-on-chain.json'; import expectedSigJson from './data/atomic-query-v3-sig-on-chain.json'; import { expect } from 'chai'; -import { byteDecoder, byteEncoder } from '../../src'; +import { byteDecoder, byteEncoder, ProofType } from '../../src'; describe('atomic-query-v3', () => { it('TestAttrQueryV3OnChain_SigPart_PrepareInputs', async () => { @@ -101,7 +100,7 @@ describe('atomic-query-v3', () => { query.values = prepareIntArray([BigInt(10)], 64); inputs.query = query; inputs.currentTimeStamp = timestamp; - inputs.proofType = AtomicProofType.BJJSignature2021; + inputs.proofType = ProofType.BJJSignature; inputs.authClaim = user.authClaim; inputs.authClaimIncMtp = authClaimIncMTP.proof; @@ -196,7 +195,7 @@ describe('atomic-query-v3', () => { query.values = prepareIntArray([BigInt(10)], 64); inputs.query = query; inputs.currentTimeStamp = timestamp; - inputs.proofType = AtomicProofType.Iden3SparseMerkleTreeProof; + inputs.proofType = ProofType.Iden3SparseMerkleTreeProof; inputs.authClaim = user.authClaim; inputs.authClaimIncMtp = authClaimIncMTP.proof; diff --git a/tests/circuits/atomic-query-v3.test.ts b/tests/circuits/atomic-query-v3.test.ts index 85340079..251d979a 100644 --- a/tests/circuits/atomic-query-v3.test.ts +++ b/tests/circuits/atomic-query-v3.test.ts @@ -1,7 +1,6 @@ import { Id, SchemaHash } from '@iden3/js-iden3-core'; import { Hash } from '@iden3/js-merkletree'; import { - AtomicProofType, AtomicQueryV3Inputs, AtomicQueryV3PubSignals, Operators, @@ -20,7 +19,7 @@ import { import expectedMtpJson from './data/atomic-query-v3-mtp.json'; import expectedSigJson from './data/atomic-query-v3-sig.json'; import { expect } from 'chai'; -import { byteDecoder, byteEncoder } from '../../src'; +import { byteDecoder, byteEncoder, ProofType } from '../../src'; describe('atomic-query-v3', () => { it('TestAttrQueryV3_SigPart_PrepareInputs', async () => { @@ -89,7 +88,7 @@ describe('atomic-query-v3', () => { query.values = prepareIntArray([BigInt(10)], 64); inputs.query = query; inputs.currentTimeStamp = timestamp; - inputs.proofType = AtomicProofType.BJJSignature2021; + inputs.proofType = ProofType.BJJSignature; inputs.linkNonce = BigInt(0); inputs.verifierID = Id.fromBigInt( BigInt('21929109382993718606847853573861987353620810345503358891473103689157378049') @@ -155,7 +154,7 @@ describe('atomic-query-v3', () => { query.values = prepareIntArray([BigInt(10)], 64); inputs.query = query; inputs.currentTimeStamp = timestamp; - inputs.proofType = AtomicProofType.Iden3SparseMerkleTreeProof; + inputs.proofType = ProofType.Iden3SparseMerkleTreeProof; inputs.linkNonce = BigInt(0); inputs.verifierID = Id.fromBigInt( BigInt('21929109382993718606847853573861987353620810345503358891473103689157378049') diff --git a/tests/proofs/mtp.test.ts b/tests/proofs/mtp.test.ts index c9acede8..caa8e5fc 100644 --- a/tests/proofs/mtp.test.ts +++ b/tests/proofs/mtp.test.ts @@ -116,6 +116,10 @@ describe('mtp proofs', () => { }); it('mtpv2-non-merklized', async () => { + await nonMerklizedTest(CircuitId.AtomicQueryMTPV2); + }); + + const nonMerklizedTest = async (circuitId: CircuitId) => { const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); @@ -193,7 +197,7 @@ describe('mtp proofs', () => { const proofReq: ZeroKnowledgeProofRequest = { id: 1, - circuitId: CircuitId.AtomicQueryMTPV2, + circuitId: circuitId, optional: false, query: { allowedIssuers: ['*'], @@ -214,14 +218,15 @@ describe('mtp proofs', () => { const { proof, pub_signals, vp } = await proofService.generateProof(proofReq, userDID); expect(vp).to.be.undefined; - const isValid = await proofService.verifyProof( - { proof, pub_signals }, - CircuitId.AtomicQueryMTPV2 - ); + const isValid = await proofService.verifyProof({ proof, pub_signals }, circuitId); expect(isValid).to.be.true; - }); + }; it('mtpv2-merklized', async () => { + await merklizedTest(CircuitId.AtomicQueryMTPV2); + }); + + const merklizedTest = async (circuitId: CircuitId) => { const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedsnew'); const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); @@ -330,5 +335,13 @@ describe('mtp proofs', () => { CircuitId.AtomicQueryMTPV2 ); expect(isValid).to.be.true; + }; + + it('mtpv3-non-merklized', async () => { + await nonMerklizedTest(CircuitId.AtomicQueryV3); + }); + + it('mtpv3-merklized', async () => { + await merklizedTest(CircuitId.AtomicQueryV3); }); }); diff --git a/tests/proofs/sig.test.ts b/tests/proofs/sig.test.ts index f30f8323..1dcb8c5a 100644 --- a/tests/proofs/sig.test.ts +++ b/tests/proofs/sig.test.ts @@ -119,6 +119,10 @@ describe('sig proofs', () => { }); it('sigv2-non-merklized', async () => { + await sigNonMerklizedTest(CircuitId.AtomicQuerySigV2); + }); + + const sigNonMerklizedTest = async (circuitId: CircuitId) => { const claimReq: CredentialRequest = { credentialSchema: 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/kyc-nonmerklized.json', @@ -140,7 +144,7 @@ describe('sig proofs', () => { const proofReq: ZeroKnowledgeProofRequest = { id: 1, - circuitId: CircuitId.AtomicQuerySigV2, + circuitId: circuitId, optional: false, query: { allowedIssuers: ['*'], @@ -165,9 +169,21 @@ describe('sig proofs', () => { expect(proof).not.to.be.undefined; expect(vp).to.be.undefined; + }; + + it('sigv3-non-merklized', async () => { + await sigNonMerklizedTest(CircuitId.AtomicQueryV3); }); it('sigv2-merklized', async () => { + await sigMerklizedTest(CircuitId.AtomicQuerySigV2); + }); + + it('sigv3-merklized', async () => { + await sigMerklizedTest(CircuitId.AtomicQueryV3); + }); + + const sigMerklizedTest = async (circuitId: CircuitId) => { const claimReq: CredentialRequest = { credentialSchema: 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json', @@ -189,7 +205,7 @@ describe('sig proofs', () => { const proofReq: ZeroKnowledgeProofRequest = { id: 1, - circuitId: CircuitId.AtomicQuerySigV2, + circuitId: circuitId, optional: false, query: { allowedIssuers: ['*'], @@ -217,7 +233,7 @@ describe('sig proofs', () => { expect(vp).to.be.undefined; expect(proof).not.to.be.undefined; - }); + }; it('sigv2-merklized-query-array', async () => { const claimReq: CredentialRequest = { From 57d83ecf49690dd50b30411c217170ea53080223 Mon Sep 17 00:00:00 2001 From: Dimasik Kolezhniuk Date: Mon, 4 Dec 2023 19:01:07 +0100 Subject: [PATCH 05/10] Add more operators --- src/circuits/comparer.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/circuits/comparer.ts b/src/circuits/comparer.ts index 7573d021..41c40f31 100644 --- a/src/circuits/comparer.ts +++ b/src/circuits/comparer.ts @@ -10,7 +10,12 @@ export enum Operators { GT = 3, IN = 4, NIN = 5, - NE = 6 + NE = 6, + LTE = 7, + GTE = 8, + BETWEEN = 9, + SD = 16, + NULLIFY = 17 } /** QueryOperators represents operators for atomic circuits */ @@ -21,7 +26,12 @@ export const QueryOperators = { $gt: Operators.GT, $in: Operators.IN, $nin: Operators.NIN, - $ne: Operators.NE + $ne: Operators.NE, + $lte: Operators.LTE, + $gte: Operators.GTE, + $between: Operators.BETWEEN, + $sd: Operators.SD, + $nullify: Operators.NULLIFY }; // Comparer value. From 241fe048ec5dc549c19253565c24a44b131c14bb Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Dec 2023 10:57:18 +0200 Subject: [PATCH 06/10] add v3 on-chain tests --- src/circuits/models.ts | 4 +- src/proof/proof-service.ts | 96 ++++++++++++++++++++++++++++++++ src/storage/filters/jsonQuery.ts | 2 + tests/proofs/mtp-onchain.test.ts | 20 ++++--- tests/proofs/sig-onchain.test.ts | 20 ++++--- 5 files changed, 127 insertions(+), 15 deletions(-) diff --git a/src/circuits/models.ts b/src/circuits/models.ts index 4bb0b988..17032771 100644 --- a/src/circuits/models.ts +++ b/src/circuits/models.ts @@ -68,7 +68,9 @@ export enum CircuitId { // AtomicQuerySigOnChain is a type for credentialAtomicQuerySigOnChain.circom AtomicQuerySigV2OnChain = 'credentialAtomicQuerySigV2OnChain', // AtomicQueryV3CircuitID is a type for credentialAtomicQueryV3.circom - AtomicQueryV3 = 'credentialAtomicQueryV3' + AtomicQueryV3 = 'credentialAtomicQueryV3', + // AtomicQueryV3OnChainCircuitID is a type for credentialAtomicQueryV3OnChain.circom + AtomicQueryV3OnChain = 'credentialAtomicQueryV3OnChain' } /** diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index 30a7f454..76c4b1be 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -13,6 +13,7 @@ import { AtomicQuerySigV2Inputs, AtomicQuerySigV2OnChainInputs, AtomicQueryV3Inputs, + AtomicQueryV3OnChainInputs, AuthV2Inputs, CircuitClaim, CircuitId, @@ -348,6 +349,9 @@ export class ProofService implements IProofService { case CircuitId.AtomicQueryV3: generateInputFn = this.generateQueryV3Inputs.bind(this); break; + case CircuitId.AtomicQueryV3OnChain: + generateInputFn = this.generateQueryV3OnChainInputs.bind(this); + break; default: throw new Error(`circuit with id ${proofReq.circuitId} is not supported by issuer`); } @@ -626,6 +630,98 @@ export class ProofService implements IProofService { return { inputs: circuitInputs.inputsMarshal(), vp }; } + private async generateQueryV3OnChainInputs( + preparedCredential: PreparedCredential, + identifier: DID, + proofReq: ZeroKnowledgeProofRequest, + params: InputsParams + ): Promise<{ inputs: Uint8Array; vp?: object }> { + const circuitClaimData = await this.newCircuitClaimData( + preparedCredential.credential, + preparedCredential.credentialCoreClaim + ); + + circuitClaimData.nonRevProof = toClaimNonRevStatus(preparedCredential.revStatus); + let proofType: ProofType; + switch (proofReq.query.proofType) { + case ProofType.BJJSignature: + proofType = ProofType.BJJSignature; + break; + case ProofType.Iden3SparseMerkleTreeProof: + proofType = ProofType.Iden3SparseMerkleTreeProof; + break; + default: + if (circuitClaimData.proof) { + proofType = ProofType.Iden3SparseMerkleTreeProof; + } else if (circuitClaimData.signatureProof) { + proofType = ProofType.BJJSignature; + } else { + throw Error('claim has no MTP or signature proof'); + } + break; + } + + const circuitInputs = new AtomicQueryV3OnChainInputs(); + circuitInputs.id = DID.idFromDID(identifier); + circuitInputs.claim = { + issuerID: circuitClaimData?.issuerId, + signatureProof: circuitClaimData.signatureProof, + claim: circuitClaimData.claim, + nonRevProof: circuitClaimData.nonRevProof, + incProof: { proof: circuitClaimData.proof, treeState: circuitClaimData.treeState } + }; + circuitInputs.requestID = BigInt(proofReq.id); + circuitInputs.claimSubjectProfileNonce = BigInt(params.credentialSubjectProfileNonce); + circuitInputs.profileNonce = BigInt(params.authProfileNonce); + circuitInputs.skipClaimRevocationCheck = params.skipRevocation; + const { query, vp } = await this.toCircuitsQuery( + proofReq.query, + preparedCredential.credential, + preparedCredential.credentialCoreClaim + ); + circuitInputs.query = query; + circuitInputs.currentTimeStamp = getUnixTimestamp(new Date()); + + circuitInputs.proofType = proofType; + circuitInputs.linkNonce = BigInt(0); + circuitInputs.verifierID = DID.idFromDID(identifier); + circuitInputs.verifierSessionID = BigInt(0); + circuitInputs.authEnabled = 1; + + // auth inputs + if (circuitInputs.authEnabled === 1) { + const { nonce: authProfileNonce, genesisDID } = + await this._identityWallet.getGenesisDIDMetadata(identifier); + + const challenge = BigInt(proofReq.query.challenge?.toString() ?? 0); + const authPrepared = await this.prepareAuthBJJCredential(genesisDID); + + const authClaimData = await this.newCircuitClaimData( + authPrepared.authCredential, + authPrepared.authCoreClaim + ); + + const signature = await this._identityWallet.signChallenge( + challenge, + authPrepared.authCredential + ); + const id = DID.idFromDID(genesisDID); + const stateProof = await this._stateStorage.getGISTProof(id.bigInt()); + + const gistProof = toGISTProof(stateProof); + + circuitInputs.profileNonce = BigInt(authProfileNonce); + circuitInputs.authClaim = authClaimData.claim; + circuitInputs.authClaimIncMtp = authClaimData.proof; + circuitInputs.authClaimNonRevMtp = authPrepared.nonRevProof.proof; + circuitInputs.treeState = authClaimData.treeState; + circuitInputs.signature = signature; + circuitInputs.challenge = challenge; + circuitInputs.gistProof = gistProof; + } + return { inputs: circuitInputs.inputsMarshal(), vp }; + } + // NewCircuitClaimData generates circuits claim structure private async newCircuitClaimData( credential: W3CCredential, diff --git a/src/storage/filters/jsonQuery.ts b/src/storage/filters/jsonQuery.ts index 9e18aae6..7ba6d7e4 100644 --- a/src/storage/filters/jsonQuery.ts +++ b/src/storage/filters/jsonQuery.ts @@ -148,6 +148,8 @@ export const StandardJSONCredentialsQueryFilter = (query: ProofQuery): FilterQue ); case 'skipClaimRevocationCheck': return acc; + case 'challenge': + return acc; case 'credentialSubject': { const reqFilters = Object.keys(queryValue).reduce((acc: FilterQuery[], fieldKey) => { const fieldParams = queryValue[fieldKey]; diff --git a/tests/proofs/mtp-onchain.test.ts b/tests/proofs/mtp-onchain.test.ts index 71246b74..27766b0f 100644 --- a/tests/proofs/mtp-onchain.test.ts +++ b/tests/proofs/mtp-onchain.test.ts @@ -116,6 +116,14 @@ describe('mtp onchain proofs', () => { }); it('mtpv2onchain-merklized', async () => { + await onChainMerklizedTest(CircuitId.AtomicQueryMTPV2OnChain); + }); + + it('mtpv3onchain-merklized', async () => { + await onChainMerklizedTest(CircuitId.AtomicQueryV3OnChain); + }); + + const onChainMerklizedTest = async (circuitId: CircuitId) => { const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedsnew'); const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); @@ -194,7 +202,7 @@ describe('mtp onchain proofs', () => { const proofReq: ZeroKnowledgeProofRequest = { id: 1, - circuitId: CircuitId.AtomicQueryMTPV2OnChain, + circuitId: circuitId, optional: false, query: { allowedIssuers: ['*'], @@ -205,7 +213,8 @@ describe('mtp onchain proofs', () => { documentType: { $eq: 99 } - } + }, + challenge: '3331' } }; @@ -215,10 +224,7 @@ describe('mtp onchain proofs', () => { }); expect(vp).to.be.undefined; - const isValid = await proofService.verifyProof( - { proof, pub_signals }, - CircuitId.AtomicQueryMTPV2OnChain - ); + const isValid = await proofService.verifyProof({ proof, pub_signals }, circuitId); expect(isValid).to.be.true; - }); + }; }); diff --git a/tests/proofs/sig-onchain.test.ts b/tests/proofs/sig-onchain.test.ts index fcd6e1fe..d79cfa32 100644 --- a/tests/proofs/sig-onchain.test.ts +++ b/tests/proofs/sig-onchain.test.ts @@ -101,6 +101,14 @@ describe('sig onchain proofs', () => { }); it('sigv2-onchain-merklized', async () => { + await onChainMerklizedTest(CircuitId.AtomicQuerySigV2OnChain); + }); + + it('sigv3-onchain-merklized', async () => { + await onChainMerklizedTest(CircuitId.AtomicQueryV3OnChain); + }); + + const onChainMerklizedTest = async (circuitId: CircuitId) => { const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); @@ -146,7 +154,7 @@ describe('sig onchain proofs', () => { const proofReq: ZeroKnowledgeProofRequest = { id: 1, - circuitId: CircuitId.AtomicQuerySigV2OnChain, + circuitId: circuitId, optional: false, query: { allowedIssuers: ['*'], @@ -157,7 +165,8 @@ describe('sig onchain proofs', () => { birthday: { $lt: 20020101 } - } + }, + challenge: '412312' } }; @@ -179,10 +188,7 @@ describe('sig onchain proofs', () => { '15045271939084694661437431358729281571840804299863053791890179002991342242959' ); - const isValid = await proofService.verifyProof( - { proof, pub_signals }, - CircuitId.AtomicQuerySigV2OnChain - ); + const isValid = await proofService.verifyProof({ proof, pub_signals }, circuitId); expect(isValid).to.be.true; - }); + }; }); From db474f445cf01c2835986c293cd18d87773a7ed8 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Dec 2023 11:44:50 +0200 Subject: [PATCH 07/10] fix challenge --- src/proof/proof-service.ts | 2 +- src/storage/filters/jsonQuery.ts | 2 -- tests/proofs/mtp-onchain.test.ts | 3 +-- tests/proofs/sig-onchain.test.ts | 3 +-- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index 76c4b1be..7ab81a1a 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -693,7 +693,7 @@ export class ProofService implements IProofService { const { nonce: authProfileNonce, genesisDID } = await this._identityWallet.getGenesisDIDMetadata(identifier); - const challenge = BigInt(proofReq.query.challenge?.toString() ?? 0); + const challenge = BigInt(params.challenge ?? 0); const authPrepared = await this.prepareAuthBJJCredential(genesisDID); const authClaimData = await this.newCircuitClaimData( diff --git a/src/storage/filters/jsonQuery.ts b/src/storage/filters/jsonQuery.ts index 7ba6d7e4..9e18aae6 100644 --- a/src/storage/filters/jsonQuery.ts +++ b/src/storage/filters/jsonQuery.ts @@ -148,8 +148,6 @@ export const StandardJSONCredentialsQueryFilter = (query: ProofQuery): FilterQue ); case 'skipClaimRevocationCheck': return acc; - case 'challenge': - return acc; case 'credentialSubject': { const reqFilters = Object.keys(queryValue).reduce((acc: FilterQuery[], fieldKey) => { const fieldParams = queryValue[fieldKey]; diff --git a/tests/proofs/mtp-onchain.test.ts b/tests/proofs/mtp-onchain.test.ts index 27766b0f..308f24f8 100644 --- a/tests/proofs/mtp-onchain.test.ts +++ b/tests/proofs/mtp-onchain.test.ts @@ -213,8 +213,7 @@ describe('mtp onchain proofs', () => { documentType: { $eq: 99 } - }, - challenge: '3331' + } } }; diff --git a/tests/proofs/sig-onchain.test.ts b/tests/proofs/sig-onchain.test.ts index d79cfa32..e4fcb588 100644 --- a/tests/proofs/sig-onchain.test.ts +++ b/tests/proofs/sig-onchain.test.ts @@ -165,8 +165,7 @@ describe('sig onchain proofs', () => { birthday: { $lt: 20020101 } - }, - challenge: '412312' + } } }; From bda0558f4b654d654acf438a41d9468cdedd8e7b Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Dec 2023 12:09:22 +0200 Subject: [PATCH 08/10] extend ProofGenerationOptions with v3 options --- src/circuits/atomic-query-v3-on-chain.ts | 4 ++-- src/circuits/atomic-query-v3.ts | 4 ++-- src/proof/proof-service.ts | 18 +++++++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index bde6f81b..d4d3d0ee 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -44,7 +44,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { currentTimeStamp!: number; proofType!: ProofType; linkNonce!: bigint; - verifierID!: Id; + verifierID?: Id; verifierSessionID!: bigint; authEnabled!: number; @@ -314,7 +314,7 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { s.value = bigIntArrayToStringArray(values); s.linkNonce = this.linkNonce.toString(); - s.verifierID = this.verifierID.bigInt().toString(); + s.verifierID = this.verifierID?.bigInt().toString() ?? '0'; s.verifierSessionID = this.verifierSessionID.toString(); s.authEnabled = this.authEnabled.toString(); diff --git a/src/circuits/atomic-query-v3.ts b/src/circuits/atomic-query-v3.ts index a46f539c..a6bd3d35 100644 --- a/src/circuits/atomic-query-v3.ts +++ b/src/circuits/atomic-query-v3.ts @@ -39,7 +39,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { currentTimeStamp!: number; proofType!: ProofType; linkNonce!: bigint; - verifierID!: Id; + verifierID?: Id; verifierSessionID!: bigint; validate(): void { @@ -231,7 +231,7 @@ export class AtomicQueryV3Inputs extends BaseConfig { s.value = bigIntArrayToStringArray(values); s.linkNonce = this.linkNonce.toString(); - s.verifierID = this.verifierID.bigInt().toString(); + s.verifierID = this.verifierID?.bigInt().toString() ?? '0'; s.verifierSessionID = this.verifierSessionID.toString(); return byteEncoder.encode(JSON.stringify(s)); diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index 7ab81a1a..15f393a7 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -70,6 +70,10 @@ export interface ProofGenerationOptions { skipRevocation: boolean; challenge?: bigint; credential?: W3CCredential; + linkNonce?: bigint; + verifierDID?: DID; + verifierSessionID?: bigint; + authEnabled?: number; } export interface DIDProfileMetadata { @@ -624,9 +628,9 @@ export class ProofService implements IProofService { circuitInputs.currentTimeStamp = getUnixTimestamp(new Date()); circuitInputs.proofType = proofType; - circuitInputs.linkNonce = BigInt(0); - circuitInputs.verifierID = DID.idFromDID(identifier); - circuitInputs.verifierSessionID = BigInt(0); + circuitInputs.linkNonce = params.linkNonce ?? BigInt(0); + circuitInputs.verifierID = params.verifierDID ? DID.idFromDID(params.verifierDID) : undefined; + circuitInputs.verifierSessionID = params.verifierSessionID ?? BigInt(0); return { inputs: circuitInputs.inputsMarshal(), vp }; } @@ -683,10 +687,10 @@ export class ProofService implements IProofService { circuitInputs.currentTimeStamp = getUnixTimestamp(new Date()); circuitInputs.proofType = proofType; - circuitInputs.linkNonce = BigInt(0); - circuitInputs.verifierID = DID.idFromDID(identifier); - circuitInputs.verifierSessionID = BigInt(0); - circuitInputs.authEnabled = 1; + circuitInputs.linkNonce = params.linkNonce ?? BigInt(0); + circuitInputs.verifierID = params.verifierDID ? DID.idFromDID(params.verifierDID) : undefined; + circuitInputs.verifierSessionID = params.verifierSessionID ?? BigInt(0); + circuitInputs.authEnabled = params.authEnabled ?? 1; // auth inputs if (circuitInputs.authEnabled === 1) { From 0d0380baf159b2cfec0471c69b6622689253169a Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Dec 2023 14:32:53 +0200 Subject: [PATCH 09/10] linkNonce & verifierSessionID are part of query --- src/proof/proof-service.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index 15f393a7..6ce12317 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -70,9 +70,7 @@ export interface ProofGenerationOptions { skipRevocation: boolean; challenge?: bigint; credential?: W3CCredential; - linkNonce?: bigint; verifierDID?: DID; - verifierSessionID?: bigint; authEnabled?: number; } @@ -628,9 +626,13 @@ export class ProofService implements IProofService { circuitInputs.currentTimeStamp = getUnixTimestamp(new Date()); circuitInputs.proofType = proofType; - circuitInputs.linkNonce = params.linkNonce ?? BigInt(0); + circuitInputs.linkNonce = proofReq.query.linkNonce + ? BigInt(proofReq.query.linkNonce.toString()) + : BigInt(0); circuitInputs.verifierID = params.verifierDID ? DID.idFromDID(params.verifierDID) : undefined; - circuitInputs.verifierSessionID = params.verifierSessionID ?? BigInt(0); + circuitInputs.verifierSessionID = proofReq.query.verifierSessionID + ? BigInt(proofReq.query.verifierSessionID?.toString()) + : BigInt(0); return { inputs: circuitInputs.inputsMarshal(), vp }; } @@ -687,9 +689,13 @@ export class ProofService implements IProofService { circuitInputs.currentTimeStamp = getUnixTimestamp(new Date()); circuitInputs.proofType = proofType; - circuitInputs.linkNonce = params.linkNonce ?? BigInt(0); + circuitInputs.linkNonce = proofReq.query.linkNonce + ? BigInt(proofReq.query.linkNonce.toString()) + : BigInt(0); circuitInputs.verifierID = params.verifierDID ? DID.idFromDID(params.verifierDID) : undefined; - circuitInputs.verifierSessionID = params.verifierSessionID ?? BigInt(0); + circuitInputs.verifierSessionID = proofReq.query.verifierSessionID + ? BigInt(proofReq.query.verifierSessionID?.toString()) + : BigInt(0); circuitInputs.authEnabled = params.authEnabled ?? 1; // auth inputs From 18fe29ee99549f6623f8bb0c2a428fbd5fb73675 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 5 Dec 2023 16:21:59 +0200 Subject: [PATCH 10/10] fix challenge & gistRoot inputs (v3 on-chain) --- src/circuits/atomic-query-v3-on-chain.ts | 7 +- src/iden3comm/handlers/contract-request.ts | 3 +- src/proof/proof-service.ts | 25 ++-- tests/handlers/contract-request.test.ts | 152 +++++++++++++++++++++ 4 files changed, 166 insertions(+), 21 deletions(-) diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index d4d3d0ee..3205a701 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -142,11 +142,9 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { s.authClaimIncMtp = prepareSiblingsStr(new Proof(), this.getMTLevel()); s.authClaimNonRevMtp = prepareSiblingsStr(new Proof(), this.getMTLevel()); - s.challenge = zero; s.challengeSignatureR8x = zero; s.challengeSignatureR8y = zero; s.challengeSignatureS = zero; - s.gistRoot = ZERO_HASH.bigInt().toString(); s.gistMtp = prepareSiblingsStr(new Proof(), this.getMTLevelOnChain()); s.authClaimNonRevMtpAuxHi = ZERO_HASH.bigInt().toString(); @@ -211,6 +209,8 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { isRevocationChecked: 1 }; + s.challenge = this.challenge?.toString(); + s.gistRoot = this.gistProof.root.bigInt().toString(); if (this.authEnabled === 1) { s.authClaim = this.authClaim?.marshalJson(); s.userClaimsTreeRoot = this.treeState.claimsRoot?.bigInt().toString(); @@ -219,14 +219,11 @@ export class AtomicQueryV3OnChainInputs extends BaseConfig { s.userState = this.treeState.state?.bigInt().toString(); s.authClaimIncMtp = prepareSiblingsStr(this.authClaimIncMtp, this.getMTLevel()); s.authClaimNonRevMtp = prepareSiblingsStr(this.authClaimNonRevMtp, this.getMTLevel()); - s.challenge = this.challenge?.toString(); s.challengeSignatureR8x = this.signature.R8[0].toString(); s.challengeSignatureR8y = this.signature.R8[1].toString(); s.challengeSignatureS = this.signature.S.toString(); - s.gistRoot = this.gistProof.root.bigInt().toString(); s.gistMtp = this.gistProof && prepareSiblingsStr(this.gistProof.proof, this.getMTLevelOnChain()); - const nodeAuxAuth = getNodeAuxValue(this.authClaimNonRevMtp); s.authClaimNonRevMtpAuxHi = nodeAuxAuth.key.bigInt().toString(); s.authClaimNonRevMtpAuxHv = nodeAuxAuth.value.bigInt().toString(); diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index d1592bb2..710235c6 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -58,7 +58,8 @@ export type ContractInvokeHandlerOptions = { export class ContractRequestHandler implements IContractRequestHandler { private readonly _allowedCircuits = [ CircuitId.AtomicQueryMTPV2OnChain, - CircuitId.AtomicQuerySigV2OnChain + CircuitId.AtomicQuerySigV2OnChain, + CircuitId.AtomicQueryV3OnChain ]; /** diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index 6ce12317..b0eac0b6 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -696,29 +696,26 @@ export class ProofService implements IProofService { circuitInputs.verifierSessionID = proofReq.query.verifierSessionID ? BigInt(proofReq.query.verifierSessionID?.toString()) : BigInt(0); - circuitInputs.authEnabled = params.authEnabled ?? 1; + circuitInputs.authEnabled = params.authEnabled ?? 0; + circuitInputs.challenge = BigInt(params.challenge ?? 0); + const { nonce: authProfileNonce, genesisDID } = + await this._identityWallet.getGenesisDIDMetadata(identifier); + const authPrepared = await this.prepareAuthBJJCredential(genesisDID); + const id = DID.idFromDID(genesisDID); + const stateProof = await this._stateStorage.getGISTProof(id.bigInt()); + const gistProof = toGISTProof(stateProof); + circuitInputs.gistProof = gistProof; // auth inputs if (circuitInputs.authEnabled === 1) { - const { nonce: authProfileNonce, genesisDID } = - await this._identityWallet.getGenesisDIDMetadata(identifier); - - const challenge = BigInt(params.challenge ?? 0); - const authPrepared = await this.prepareAuthBJJCredential(genesisDID); - const authClaimData = await this.newCircuitClaimData( authPrepared.authCredential, authPrepared.authCoreClaim ); - const signature = await this._identityWallet.signChallenge( - challenge, + circuitInputs.challenge, authPrepared.authCredential ); - const id = DID.idFromDID(genesisDID); - const stateProof = await this._stateStorage.getGISTProof(id.bigInt()); - - const gistProof = toGISTProof(stateProof); circuitInputs.profileNonce = BigInt(authProfileNonce); circuitInputs.authClaim = authClaimData.claim; @@ -726,8 +723,6 @@ export class ProofService implements IProofService { circuitInputs.authClaimNonRevMtp = authPrepared.nonRevProof.proof; circuitInputs.treeState = authClaimData.treeState; circuitInputs.signature = signature; - circuitInputs.challenge = challenge; - circuitInputs.gistProof = gistProof; } return { inputs: circuitInputs.inputsMarshal(), vp }; } diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 055c525c..75b627fe 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -437,6 +437,158 @@ describe('contract-request', () => { options ); + expect(ciResponse).not.be.undefined; + expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( + proofReq.id + ); + }); + // V3 integration test + it.skip('contract request flow V3 - integration test', async () => { + const stateEthConfig = defaultEthConnectionConfig; + stateEthConfig.url = rpcUrl; + stateEthConfig.contractAddress = '0x134b1be34911e39a8397ec6289782989729807a4'; + + const memoryKeyStore = new InMemoryPrivateKeyStore(); + const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); + const kms = new KMS(); + kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + dataStorage = { + credential: new CredentialStorage(new InMemoryDataSource()), + identity: new IdentityStorage( + new InMemoryDataSource(), + new InMemoryDataSource() + ), + mt: new InMemoryMerkleTreeStorage(40), + states: new EthStateStorage(stateEthConfig) + }; + const circuitStorage = new FSCircuitStorage({ + dirname: path.join(__dirname, '../proofs/testdata') + }); + + const resolvers = new CredentialStatusResolverRegistry(); + resolvers.register( + CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + new RHSResolver(dataStorage.states) + ); + credWallet = new CredentialWallet(dataStorage, resolvers); + idWallet = new IdentityWallet(kms, dataStorage, credWallet); + + proofService = new ProofService(idWallet, credWallet, circuitStorage, dataStorage.states, { + ipfsNodeURL + }); + packageMgr = await getPackageMgr( + await circuitStorage.loadCircuitData(CircuitId.AuthV2), + proofService.generateAuthV2Inputs.bind(proofService), + proofService.verifyState.bind(proofService) + ); + contractRequest = new ContractRequestHandler(packageMgr, proofService, mockZKPVerifier); + + const { did: userDID, credential: cred } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Polygon, + networkId: NetworkId.Mumbai, + seed: seedPhrase, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + + expect(cred).not.to.be.undefined; + + const { did: issuerDID, credential: issuerAuthCredential } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Polygon, + networkId: NetworkId.Mumbai, + seed: seedPhraseIssuer, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + expect(issuerAuthCredential).not.to.be.undefined; + + const claimReq: CredentialRequest = { + credentialSchema: + 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json', + type: 'KYCAgeCredential', + credentialSubject: { + id: userDID.string(), + birthday: 19960424, + documentType: 99 + }, + expiration: 2793526400, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }; + const issuerCred = await idWallet.issueCredential(issuerDID, claimReq); + + await credWallet.save(issuerCred); + + const proofReq: ZeroKnowledgeProofRequest = { + id: 1, + circuitId: CircuitId.AtomicQueryV3OnChain, + optional: false, + query: { + allowedIssuers: ['*'], + type: claimReq.type, + context: + 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld', + credentialSubject: { + birthday: { + $lt: 20020101 + } + } + } + }; + + const contractAddress = '0xEE6164690aBB9CCcBe9248D8eC0Da797f9F6C575'; + const conf = defaultEthConnectionConfig; + conf.contractAddress = contractAddress; + conf.url = rpcUrl; + conf.chainId = 80001; + + const zkpVerifier = new OnChainZKPVerifier([conf]); + contractRequest = new ContractRequestHandler(packageMgr, proofService, zkpVerifier); + + const transactionData: ContractInvokeTransactionData = { + contract_address: contractAddress, + method_id: 'b68967e2', + chain_id: conf.chainId + }; + + const ciRequestBody: ContractInvokeRequestBody = { + reason: 'reason', + transaction_data: transactionData, + scope: [proofReq as ZeroKnowledgeProofRequest] + }; + + const id = uuid.v4(); + const ciRequest: ContractInvokeRequest = { + id, + typ: MediaType.PlainMessage, + type: PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE, + thid: id, + body: ciRequestBody + }; + + const ethSigner = new ethers.Wallet(walletKey); + + const challenge = BytesHelper.bytesToInt(hexToBytes(ethSigner.address)); + + const options: ContractInvokeHandlerOptions = { + ethSigner, + challenge + }; + const msgBytes = byteEncoder.encode(JSON.stringify(ciRequest)); + const ciResponse = await contractRequest.handleContractInvokeRequest( + userDID, + msgBytes, + options + ); + expect(ciResponse).not.be.undefined; expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( proofReq.id