@0xpolygonid/js-sdk 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,7 +30,8 @@ const js_iden3_core_1 = require("@iden3/js-iden3-core");
30
30
  const js_jwz_1 = require("@iden3/js-jwz");
31
31
  const uuid = __importStar(require("uuid"));
32
32
  const utils_1 = require("../../utils");
33
- const js_crypto_1 = require("@iden3/js-crypto");
33
+ const common_1 = require("./common");
34
+ const circuits_1 = require("../../circuits");
34
35
  /**
35
36
  * createAuthorizationRequest is a function to create protocol authorization request
36
37
  * @param {string} reason - reason to request proof
@@ -87,6 +88,12 @@ class AuthHandler {
87
88
  constructor(_packerMgr, _proofService) {
88
89
  this._packerMgr = _packerMgr;
89
90
  this._proofService = _proofService;
91
+ this._supportedCircuits = [
92
+ circuits_1.CircuitId.AtomicQueryV3,
93
+ circuits_1.CircuitId.AtomicQuerySigV2,
94
+ circuits_1.CircuitId.AtomicQueryMTPV2,
95
+ circuits_1.CircuitId.LinkedMultiQuery10
96
+ ];
90
97
  }
91
98
  /**
92
99
  * @inheritdoc IAuthHandler#parseAuthorizationRequest
@@ -121,6 +128,11 @@ class AuthHandler {
121
128
  did = js_iden3_core_1.DID.parse(authRequest.to);
122
129
  }
123
130
  const guid = uuid.v4();
131
+ if (!authRequest.from) {
132
+ throw new Error('auth request should contain from field');
133
+ }
134
+ const from = js_iden3_core_1.DID.parse(authRequest.from);
135
+ const responseScope = await (0, common_1.processZeroKnowledgeProofRequests)(did, authRequest?.body.scope, from, this._proofService, { ...opts, supportedCircuits: this._supportedCircuits });
124
136
  const authResponse = {
125
137
  id: guid,
126
138
  typ: opts.mediaType,
@@ -128,64 +140,11 @@ class AuthHandler {
128
140
  thid: authRequest.thid ?? guid,
129
141
  body: {
130
142
  message: authRequest?.body?.message,
131
- scope: []
143
+ scope: responseScope
132
144
  },
133
145
  from: did.string(),
134
146
  to: authRequest.from
135
147
  };
136
- const requestScope = authRequest.body.scope;
137
- const combinedQueries = requestScope.reduce((acc, proofReq) => {
138
- const groupId = proofReq.query.groupId;
139
- if (!groupId) {
140
- return acc;
141
- }
142
- const existedData = acc.get(groupId);
143
- if (!existedData) {
144
- const seed = (0, js_crypto_1.getRandomBytes)(12);
145
- const dataView = new DataView(seed.buffer);
146
- const linkNonce = dataView.getUint32(0);
147
- acc.set(groupId, { query: proofReq.query, linkNonce });
148
- return acc;
149
- }
150
- const credentialSubject = (0, utils_1.mergeObjects)(existedData.query.credentialSubject, proofReq.query.credentialSubject);
151
- acc.set(groupId, {
152
- ...existedData,
153
- query: {
154
- skipClaimRevocationCheck: existedData.query.skipClaimRevocationCheck || proofReq.query.skipClaimRevocationCheck,
155
- ...existedData.query,
156
- credentialSubject
157
- }
158
- });
159
- return acc;
160
- }, new Map());
161
- const groupedCredentialsCache = new Map();
162
- for (const proofReq of requestScope) {
163
- const query = proofReq.query;
164
- const groupId = query.groupId;
165
- const combinedQueryData = combinedQueries.get(groupId);
166
- if (groupId) {
167
- if (!combinedQueryData) {
168
- throw new Error(`Invalid group id ${query.groupId}`);
169
- }
170
- const combinedQuery = combinedQueryData.query;
171
- if (!groupedCredentialsCache.has(groupId)) {
172
- const credWithRevStatus = await this._proofService.findCredentialByProofQuery(did, combinedQueryData.query);
173
- if (!credWithRevStatus.cred) {
174
- throw new Error(`Credential not found for query ${JSON.stringify(combinedQuery)}`);
175
- }
176
- groupedCredentialsCache.set(groupId, credWithRevStatus);
177
- }
178
- }
179
- const credWithRevStatus = groupedCredentialsCache.get(groupId);
180
- const zkpRes = await this._proofService.generateProof(proofReq, did, {
181
- verifierDid: js_iden3_core_1.DID.parse(authRequest.from),
182
- skipRevocation: Boolean(query.skipClaimRevocationCheck),
183
- credential: credWithRevStatus?.cred,
184
- credentialRevocationStatus: credWithRevStatus?.revStatus,
185
- linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
186
- });
187
- authResponse.body.scope.push(zkpRes);
188
- }
189
148
  const msgBytes = utils_1.byteEncoder.encode(JSON.stringify(authResponse));
190
149
  const packerOpts = opts.mediaType === constants_1.MediaType.SignedMessage
191
150
  ? opts.packerOptions
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processZeroKnowledgeProofRequests = void 0;
4
+ const js_crypto_1 = require("@iden3/js-crypto");
5
+ const utils_1 = require("../../utils");
6
+ /**
7
+ * Groups the ZeroKnowledgeProofRequest objects based on their groupId.
8
+ * Returns a Map where the key is the groupId and the value is an object containing the query and linkNonce.
9
+ *
10
+ * @param requestScope - An array of ZeroKnowledgeProofRequest objects.
11
+ * @returns A Map<number, { query: JSONObject; linkNonce: number }> representing the grouped queries.
12
+ */
13
+ const getGroupedQueries = (requestScope) => requestScope.reduce((acc, proofReq) => {
14
+ const groupId = proofReq.query.groupId;
15
+ if (!groupId) {
16
+ return acc;
17
+ }
18
+ const existedData = acc.get(groupId);
19
+ if (!existedData) {
20
+ const seed = (0, js_crypto_1.getRandomBytes)(12);
21
+ const dataView = new DataView(seed.buffer);
22
+ const linkNonce = dataView.getUint32(0);
23
+ acc.set(groupId, { query: proofReq.query, linkNonce });
24
+ return acc;
25
+ }
26
+ const credentialSubject = (0, utils_1.mergeObjects)(existedData.query.credentialSubject, proofReq.query.credentialSubject);
27
+ acc.set(groupId, {
28
+ ...existedData,
29
+ query: {
30
+ skipClaimRevocationCheck: existedData.query.skipClaimRevocationCheck || proofReq.query.skipClaimRevocationCheck,
31
+ ...existedData.query,
32
+ credentialSubject
33
+ }
34
+ });
35
+ return acc;
36
+ }, new Map());
37
+ /**
38
+ * Processes zero knowledge proof requests.
39
+ *
40
+ * @param senderIdentifier - The identifier of the sender.
41
+ * @param requests - An array of zero knowledge proof requests.
42
+ * @param from - The identifier of the sender.
43
+ * @param proofService - The proof service.
44
+ * @param opts - Additional options for processing the requests.
45
+ * @returns A promise that resolves to an array of zero knowledge proof responses.
46
+ */
47
+ const processZeroKnowledgeProofRequests = async (senderIdentifier, requests, from, proofService, opts) => {
48
+ const requestScope = requests ?? [];
49
+ const combinedQueries = getGroupedQueries(requestScope);
50
+ const groupedCredentialsCache = new Map();
51
+ const zkpResponses = [];
52
+ for (const proofReq of requestScope) {
53
+ if (!opts.supportedCircuits.includes(proofReq.circuitId)) {
54
+ throw new Error(`Circuit ${proofReq.circuitId} is not allowed`);
55
+ }
56
+ const query = proofReq.query;
57
+ const groupId = query.groupId;
58
+ const combinedQueryData = combinedQueries.get(groupId);
59
+ if (groupId) {
60
+ if (!combinedQueryData) {
61
+ throw new Error(`Invalid group id ${query.groupId}`);
62
+ }
63
+ const combinedQuery = combinedQueryData.query;
64
+ if (!groupedCredentialsCache.has(groupId)) {
65
+ const credWithRevStatus = await proofService.findCredentialByProofQuery(senderIdentifier, combinedQueryData.query);
66
+ if (!credWithRevStatus.cred) {
67
+ throw new Error(`Credential not found for query ${JSON.stringify(combinedQuery)}`);
68
+ }
69
+ groupedCredentialsCache.set(groupId, credWithRevStatus);
70
+ }
71
+ }
72
+ const credWithRevStatus = groupedCredentialsCache.get(groupId);
73
+ const zkpRes = await proofService.generateProof(proofReq, senderIdentifier, {
74
+ verifierDid: from,
75
+ challenge: opts.challenge,
76
+ skipRevocation: Boolean(query.skipClaimRevocationCheck),
77
+ credential: credWithRevStatus?.cred,
78
+ credentialRevocationStatus: credWithRevStatus?.revStatus,
79
+ linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
80
+ });
81
+ zkpResponses.push(zkpRes);
82
+ }
83
+ return zkpResponses;
84
+ };
85
+ exports.processZeroKnowledgeProofRequests = processZeroKnowledgeProofRequests;
@@ -4,6 +4,7 @@ exports.ContractRequestHandler = void 0;
4
4
  const models_1 = require("../../circuits/models");
5
5
  const constants_1 = require("../constants");
6
6
  const js_iden3_core_1 = require("@iden3/js-iden3-core");
7
+ const common_1 = require("./common");
7
8
  /**
8
9
  *
9
10
  * Allows to process ContractInvokeRequest protocol message
@@ -25,7 +26,7 @@ class ContractRequestHandler {
25
26
  this._packerMgr = _packerMgr;
26
27
  this._proofService = _proofService;
27
28
  this._zkpVerifier = _zkpVerifier;
28
- this._allowedCircuits = [
29
+ this._supportedCircuits = [
29
30
  models_1.CircuitId.AtomicQueryMTPV2OnChain,
30
31
  models_1.CircuitId.AtomicQuerySigV2OnChain,
31
32
  models_1.CircuitId.AtomicQueryV3OnChain
@@ -62,25 +63,14 @@ class ContractRequestHandler {
62
63
  if (!opts.ethSigner) {
63
64
  throw new Error("Can't sign transaction. Provide Signer in options.");
64
65
  }
65
- const zkRequests = [];
66
66
  const { chain_id } = ciRequest.body.transaction_data;
67
67
  const networkFlag = Object.keys(js_iden3_core_1.ChainIds).find((key) => js_iden3_core_1.ChainIds[key] === chain_id);
68
68
  if (!networkFlag) {
69
69
  throw new Error(`Invalid chain id ${chain_id}`);
70
70
  }
71
- for (const proofReq of ciRequest.body.scope) {
72
- if (!this._allowedCircuits.includes(proofReq.circuitId)) {
73
- throw new Error(`Can't handle circuit ${proofReq.circuitId}. Only onchain circuits are allowed.`);
74
- }
75
- const query = proofReq.query;
76
- const zkpRes = await this._proofService.generateProof(proofReq, did, {
77
- skipRevocation: query.skipClaimRevocationCheck ?? false,
78
- challenge: opts.challenge,
79
- verifierDid: ciRequest.from ? js_iden3_core_1.DID.parse(ciRequest.from) : undefined
80
- });
81
- zkRequests.push(zkpRes);
82
- }
83
- return this._zkpVerifier.submitZKPResponse(opts.ethSigner, ciRequest.body.transaction_data, zkRequests);
71
+ const verifierDid = ciRequest.from ? js_iden3_core_1.DID.parse(ciRequest.from) : undefined;
72
+ const zkpResponses = await (0, common_1.processZeroKnowledgeProofRequests)(did, ciRequest?.body?.scope, verifierDid, this._proofService, { ...opts, supportedCircuits: this._supportedCircuits });
73
+ return this._zkpVerifier.submitZKPResponse(opts.ethSigner, ciRequest.body.transaction_data, zkpResponses);
84
74
  }
85
75
  }
86
76
  exports.ContractRequestHandler = ContractRequestHandler;
@@ -18,3 +18,5 @@ __exportStar(require("./auth"), exports);
18
18
  __exportStar(require("./fetch"), exports);
19
19
  __exportStar(require("./contract-request"), exports);
20
20
  __exportStar(require("./refresh"), exports);
21
+ __exportStar(require("./revocation-status"), exports);
22
+ __exportStar(require("./common"), exports);
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.RevocationStatusHandler = void 0;
27
+ const constants_1 = require("../constants");
28
+ const constants_2 = require("../constants");
29
+ const js_iden3_core_1 = require("@iden3/js-iden3-core");
30
+ const uuid = __importStar(require("uuid"));
31
+ const utils_1 = require("../../utils");
32
+ const js_jwz_1 = require("@iden3/js-jwz");
33
+ /**
34
+ *
35
+ * Allows to process RevocationStatusRequest protocol message
36
+ *
37
+
38
+ * @class RevocationStatusHandler
39
+ * @implements implements IRevocationStatusHandler interface
40
+ */
41
+ class RevocationStatusHandler {
42
+ /**
43
+ * Creates an instance of RevocationStatusHandler.
44
+ * @param {IPackageManager} _packerMgr - package manager to unpack message envelope
45
+ * @param {IIdentityWallet} _identityWallet - identity wallet
46
+ *
47
+ */
48
+ constructor(_packerMgr, _identityWallet) {
49
+ this._packerMgr = _packerMgr;
50
+ this._identityWallet = _identityWallet;
51
+ }
52
+ /**
53
+ * @inheritdoc IRevocationStatusHandler#parseRevocationStatusRequest
54
+ */
55
+ async parseRevocationStatusRequest(request) {
56
+ const { unpackedMessage: message } = await this._packerMgr.unpack(request);
57
+ const ciRequest = message;
58
+ if (message.type !== constants_1.PROTOCOL_MESSAGE_TYPE.REVOCATION_STATUS_REQUEST_MESSAGE_TYPE) {
59
+ throw new Error('Invalid media type');
60
+ }
61
+ return ciRequest;
62
+ }
63
+ /**
64
+ * @inheritdoc IRevocationStatusHandler#handleRevocationStatusRequest
65
+ */
66
+ async handleRevocationStatusRequest(did, request, opts) {
67
+ if (!opts) {
68
+ opts = {
69
+ mediaType: constants_2.MediaType.PlainMessage
70
+ };
71
+ }
72
+ if (opts.mediaType === constants_2.MediaType.SignedMessage && !opts.packerOptions) {
73
+ throw new Error(`jws packer options are required for ${constants_2.MediaType.SignedMessage}`);
74
+ }
75
+ const rsRequest = await this.parseRevocationStatusRequest(request);
76
+ if (!rsRequest.to) {
77
+ throw new Error(`failed request. empty 'to' field`);
78
+ }
79
+ if (!rsRequest.from) {
80
+ throw new Error(`failed request. empty 'from' field`);
81
+ }
82
+ if (!rsRequest.body?.revocation_nonce) {
83
+ throw new Error(`failed request. empty 'revocation_nonce' field`);
84
+ }
85
+ const issuerDID = js_iden3_core_1.DID.parse(rsRequest.to);
86
+ const mtpWithTreeState = await this._identityWallet.generateNonRevocationMtpWithNonce(issuerDID, BigInt(rsRequest.body.revocation_nonce), opts?.treeState);
87
+ const treeState = mtpWithTreeState.treeState;
88
+ const revStatus = {
89
+ issuer: {
90
+ state: treeState?.state.string(),
91
+ claimsTreeRoot: treeState.claimsRoot.string(),
92
+ revocationTreeRoot: treeState.revocationRoot.string(),
93
+ rootOfRoots: treeState.rootOfRoots.string()
94
+ },
95
+ mtp: mtpWithTreeState.proof
96
+ };
97
+ const packerOpts = opts.mediaType === constants_2.MediaType.SignedMessage
98
+ ? opts.packerOptions
99
+ : {
100
+ provingMethodAlg: js_jwz_1.proving.provingMethodGroth16AuthV2Instance.methodAlg
101
+ };
102
+ const senderDID = js_iden3_core_1.DID.parse(rsRequest.to);
103
+ const guid = uuid.v4();
104
+ const response = {
105
+ id: guid,
106
+ typ: constants_2.MediaType.PlainMessage,
107
+ type: constants_1.PROTOCOL_MESSAGE_TYPE.REVOCATION_STATUS_RESPONSE_MESSAGE_TYPE,
108
+ thid: rsRequest.thid ?? guid,
109
+ body: revStatus,
110
+ from: did.string(),
111
+ to: rsRequest.from
112
+ };
113
+ return this._packerMgr.pack(opts.mediaType, utils_1.byteEncoder.encode(JSON.stringify(response)), {
114
+ senderDID,
115
+ ...packerOpts
116
+ });
117
+ }
118
+ }
119
+ exports.RevocationStatusHandler = RevocationStatusHandler;
@@ -253,6 +253,10 @@ class IdentityWallet {
253
253
  async generateNonRevocationMtp(did, credential, treeState) {
254
254
  const coreClaim = await this.getCoreClaimFromCredential(credential);
255
255
  const revNonce = coreClaim.getRevocationNonce();
256
+ return this.generateNonRevocationMtpWithNonce(did, revNonce, treeState);
257
+ }
258
+ /** {@inheritDoc IIdentityWallet.generateNonRevocationMtpWithNonce} */
259
+ async generateNonRevocationMtpWithNonce(did, revNonce, treeState) {
256
260
  const treesModel = await this.getDIDTreeModel(did);
257
261
  const revocationTree = await this._storage.mt.getMerkleTreeByIdentifierAndType(did.string(), storage_1.MerkleTreeType.Revocations);
258
262
  const claimsRoot = await treesModel.claimsTree.root();
@@ -3,8 +3,9 @@ import { PROTOCOL_MESSAGE_TYPE } from '../constants';
3
3
  import { DID } from '@iden3/js-iden3-core';
4
4
  import { proving } from '@iden3/js-jwz';
5
5
  import * as uuid from 'uuid';
6
- import { byteDecoder, byteEncoder, mergeObjects } from '../../utils';
7
- import { getRandomBytes } from '@iden3/js-crypto';
6
+ import { byteDecoder, byteEncoder } from '../../utils';
7
+ import { processZeroKnowledgeProofRequests } from './common';
8
+ import { CircuitId } from '../../circuits';
8
9
  /**
9
10
  * createAuthorizationRequest is a function to create protocol authorization request
10
11
  * @param {string} reason - reason to request proof
@@ -59,6 +60,12 @@ export class AuthHandler {
59
60
  constructor(_packerMgr, _proofService) {
60
61
  this._packerMgr = _packerMgr;
61
62
  this._proofService = _proofService;
63
+ this._supportedCircuits = [
64
+ CircuitId.AtomicQueryV3,
65
+ CircuitId.AtomicQuerySigV2,
66
+ CircuitId.AtomicQueryMTPV2,
67
+ CircuitId.LinkedMultiQuery10
68
+ ];
62
69
  }
63
70
  /**
64
71
  * @inheritdoc IAuthHandler#parseAuthorizationRequest
@@ -93,6 +100,11 @@ export class AuthHandler {
93
100
  did = DID.parse(authRequest.to);
94
101
  }
95
102
  const guid = uuid.v4();
103
+ if (!authRequest.from) {
104
+ throw new Error('auth request should contain from field');
105
+ }
106
+ const from = DID.parse(authRequest.from);
107
+ const responseScope = await processZeroKnowledgeProofRequests(did, authRequest?.body.scope, from, this._proofService, { ...opts, supportedCircuits: this._supportedCircuits });
96
108
  const authResponse = {
97
109
  id: guid,
98
110
  typ: opts.mediaType,
@@ -100,64 +112,11 @@ export class AuthHandler {
100
112
  thid: authRequest.thid ?? guid,
101
113
  body: {
102
114
  message: authRequest?.body?.message,
103
- scope: []
115
+ scope: responseScope
104
116
  },
105
117
  from: did.string(),
106
118
  to: authRequest.from
107
119
  };
108
- const requestScope = authRequest.body.scope;
109
- const combinedQueries = requestScope.reduce((acc, proofReq) => {
110
- const groupId = proofReq.query.groupId;
111
- if (!groupId) {
112
- return acc;
113
- }
114
- const existedData = acc.get(groupId);
115
- if (!existedData) {
116
- const seed = getRandomBytes(12);
117
- const dataView = new DataView(seed.buffer);
118
- const linkNonce = dataView.getUint32(0);
119
- acc.set(groupId, { query: proofReq.query, linkNonce });
120
- return acc;
121
- }
122
- const credentialSubject = mergeObjects(existedData.query.credentialSubject, proofReq.query.credentialSubject);
123
- acc.set(groupId, {
124
- ...existedData,
125
- query: {
126
- skipClaimRevocationCheck: existedData.query.skipClaimRevocationCheck || proofReq.query.skipClaimRevocationCheck,
127
- ...existedData.query,
128
- credentialSubject
129
- }
130
- });
131
- return acc;
132
- }, new Map());
133
- const groupedCredentialsCache = new Map();
134
- for (const proofReq of requestScope) {
135
- const query = proofReq.query;
136
- const groupId = query.groupId;
137
- const combinedQueryData = combinedQueries.get(groupId);
138
- if (groupId) {
139
- if (!combinedQueryData) {
140
- throw new Error(`Invalid group id ${query.groupId}`);
141
- }
142
- const combinedQuery = combinedQueryData.query;
143
- if (!groupedCredentialsCache.has(groupId)) {
144
- const credWithRevStatus = await this._proofService.findCredentialByProofQuery(did, combinedQueryData.query);
145
- if (!credWithRevStatus.cred) {
146
- throw new Error(`Credential not found for query ${JSON.stringify(combinedQuery)}`);
147
- }
148
- groupedCredentialsCache.set(groupId, credWithRevStatus);
149
- }
150
- }
151
- const credWithRevStatus = groupedCredentialsCache.get(groupId);
152
- const zkpRes = await this._proofService.generateProof(proofReq, did, {
153
- verifierDid: DID.parse(authRequest.from),
154
- skipRevocation: Boolean(query.skipClaimRevocationCheck),
155
- credential: credWithRevStatus?.cred,
156
- credentialRevocationStatus: credWithRevStatus?.revStatus,
157
- linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
158
- });
159
- authResponse.body.scope.push(zkpRes);
160
- }
161
120
  const msgBytes = byteEncoder.encode(JSON.stringify(authResponse));
162
121
  const packerOpts = opts.mediaType === MediaType.SignedMessage
163
122
  ? opts.packerOptions
@@ -0,0 +1,81 @@
1
+ import { getRandomBytes } from '@iden3/js-crypto';
2
+ import { mergeObjects } from '../../utils';
3
+ /**
4
+ * Groups the ZeroKnowledgeProofRequest objects based on their groupId.
5
+ * Returns a Map where the key is the groupId and the value is an object containing the query and linkNonce.
6
+ *
7
+ * @param requestScope - An array of ZeroKnowledgeProofRequest objects.
8
+ * @returns A Map<number, { query: JSONObject; linkNonce: number }> representing the grouped queries.
9
+ */
10
+ const getGroupedQueries = (requestScope) => requestScope.reduce((acc, proofReq) => {
11
+ const groupId = proofReq.query.groupId;
12
+ if (!groupId) {
13
+ return acc;
14
+ }
15
+ const existedData = acc.get(groupId);
16
+ if (!existedData) {
17
+ const seed = getRandomBytes(12);
18
+ const dataView = new DataView(seed.buffer);
19
+ const linkNonce = dataView.getUint32(0);
20
+ acc.set(groupId, { query: proofReq.query, linkNonce });
21
+ return acc;
22
+ }
23
+ const credentialSubject = mergeObjects(existedData.query.credentialSubject, proofReq.query.credentialSubject);
24
+ acc.set(groupId, {
25
+ ...existedData,
26
+ query: {
27
+ skipClaimRevocationCheck: existedData.query.skipClaimRevocationCheck || proofReq.query.skipClaimRevocationCheck,
28
+ ...existedData.query,
29
+ credentialSubject
30
+ }
31
+ });
32
+ return acc;
33
+ }, new Map());
34
+ /**
35
+ * Processes zero knowledge proof requests.
36
+ *
37
+ * @param senderIdentifier - The identifier of the sender.
38
+ * @param requests - An array of zero knowledge proof requests.
39
+ * @param from - The identifier of the sender.
40
+ * @param proofService - The proof service.
41
+ * @param opts - Additional options for processing the requests.
42
+ * @returns A promise that resolves to an array of zero knowledge proof responses.
43
+ */
44
+ export const processZeroKnowledgeProofRequests = async (senderIdentifier, requests, from, proofService, opts) => {
45
+ const requestScope = requests ?? [];
46
+ const combinedQueries = getGroupedQueries(requestScope);
47
+ const groupedCredentialsCache = new Map();
48
+ const zkpResponses = [];
49
+ for (const proofReq of requestScope) {
50
+ if (!opts.supportedCircuits.includes(proofReq.circuitId)) {
51
+ throw new Error(`Circuit ${proofReq.circuitId} is not allowed`);
52
+ }
53
+ const query = proofReq.query;
54
+ const groupId = query.groupId;
55
+ const combinedQueryData = combinedQueries.get(groupId);
56
+ if (groupId) {
57
+ if (!combinedQueryData) {
58
+ throw new Error(`Invalid group id ${query.groupId}`);
59
+ }
60
+ const combinedQuery = combinedQueryData.query;
61
+ if (!groupedCredentialsCache.has(groupId)) {
62
+ const credWithRevStatus = await proofService.findCredentialByProofQuery(senderIdentifier, combinedQueryData.query);
63
+ if (!credWithRevStatus.cred) {
64
+ throw new Error(`Credential not found for query ${JSON.stringify(combinedQuery)}`);
65
+ }
66
+ groupedCredentialsCache.set(groupId, credWithRevStatus);
67
+ }
68
+ }
69
+ const credWithRevStatus = groupedCredentialsCache.get(groupId);
70
+ const zkpRes = await proofService.generateProof(proofReq, senderIdentifier, {
71
+ verifierDid: from,
72
+ challenge: opts.challenge,
73
+ skipRevocation: Boolean(query.skipClaimRevocationCheck),
74
+ credential: credWithRevStatus?.cred,
75
+ credentialRevocationStatus: credWithRevStatus?.revStatus,
76
+ linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
77
+ });
78
+ zkpResponses.push(zkpRes);
79
+ }
80
+ return zkpResponses;
81
+ };
@@ -1,6 +1,7 @@
1
1
  import { CircuitId } from '../../circuits/models';
2
2
  import { PROTOCOL_MESSAGE_TYPE } from '../constants';
3
3
  import { DID, ChainIds } from '@iden3/js-iden3-core';
4
+ import { processZeroKnowledgeProofRequests } from './common';
4
5
  /**
5
6
  *
6
7
  * Allows to process ContractInvokeRequest protocol message
@@ -22,7 +23,7 @@ export class ContractRequestHandler {
22
23
  this._packerMgr = _packerMgr;
23
24
  this._proofService = _proofService;
24
25
  this._zkpVerifier = _zkpVerifier;
25
- this._allowedCircuits = [
26
+ this._supportedCircuits = [
26
27
  CircuitId.AtomicQueryMTPV2OnChain,
27
28
  CircuitId.AtomicQuerySigV2OnChain,
28
29
  CircuitId.AtomicQueryV3OnChain
@@ -59,24 +60,13 @@ export class ContractRequestHandler {
59
60
  if (!opts.ethSigner) {
60
61
  throw new Error("Can't sign transaction. Provide Signer in options.");
61
62
  }
62
- const zkRequests = [];
63
63
  const { chain_id } = ciRequest.body.transaction_data;
64
64
  const networkFlag = Object.keys(ChainIds).find((key) => ChainIds[key] === chain_id);
65
65
  if (!networkFlag) {
66
66
  throw new Error(`Invalid chain id ${chain_id}`);
67
67
  }
68
- for (const proofReq of ciRequest.body.scope) {
69
- if (!this._allowedCircuits.includes(proofReq.circuitId)) {
70
- throw new Error(`Can't handle circuit ${proofReq.circuitId}. Only onchain circuits are allowed.`);
71
- }
72
- const query = proofReq.query;
73
- const zkpRes = await this._proofService.generateProof(proofReq, did, {
74
- skipRevocation: query.skipClaimRevocationCheck ?? false,
75
- challenge: opts.challenge,
76
- verifierDid: ciRequest.from ? DID.parse(ciRequest.from) : undefined
77
- });
78
- zkRequests.push(zkpRes);
79
- }
80
- return this._zkpVerifier.submitZKPResponse(opts.ethSigner, ciRequest.body.transaction_data, zkRequests);
68
+ const verifierDid = ciRequest.from ? DID.parse(ciRequest.from) : undefined;
69
+ const zkpResponses = await processZeroKnowledgeProofRequests(did, ciRequest?.body?.scope, verifierDid, this._proofService, { ...opts, supportedCircuits: this._supportedCircuits });
70
+ return this._zkpVerifier.submitZKPResponse(opts.ethSigner, ciRequest.body.transaction_data, zkpResponses);
81
71
  }
82
72
  }
@@ -2,3 +2,5 @@ export * from './auth';
2
2
  export * from './fetch';
3
3
  export * from './contract-request';
4
4
  export * from './refresh';
5
+ export * from './revocation-status';
6
+ export * from './common';