@0xpolygonid/js-sdk 1.30.0 → 1.31.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.
@@ -196,7 +196,7 @@ class CredentialWallet {
196
196
  }
197
197
  return { cred, revStatus };
198
198
  }
199
- throw new Error('all claims are revoked');
199
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.CREDENTIAL_WALLET_ALL_CREDENTIALS_ARE_REVOKED);
200
200
  }
201
201
  }
202
202
  exports.CredentialWallet = CredentialWallet;
@@ -4,6 +4,7 @@ exports.verifyExpiresTime = exports.packMetadatas = exports.calcChallengeAuthV2
4
4
  const js_crypto_1 = require("@iden3/js-crypto");
5
5
  const types_1 = require("../types");
6
6
  const utils_1 = require("../../utils");
7
+ const verifiable_1 = require("../../verifiable");
7
8
  const js_iden3_core_1 = require("@iden3/js-iden3-core");
8
9
  const constants_1 = require("../constants");
9
10
  const ethers_1 = require("ethers");
@@ -55,34 +56,60 @@ const processZeroKnowledgeProofRequests = async (to, requests, from, proofServic
55
56
  const groupedCredentialsCache = new Map();
56
57
  const zkpResponses = [];
57
58
  for (const proofReq of requestScope) {
58
- if (!opts.supportedCircuits.includes(proofReq.circuitId)) {
59
- throw new Error(`Circuit ${proofReq.circuitId} is not allowed`);
60
- }
61
- const query = proofReq.query;
62
- const groupId = query.groupId;
63
- const combinedQueryData = combinedQueries.get(groupId);
64
- if (groupId) {
65
- if (!combinedQueryData) {
66
- throw new Error(`Invalid group id ${query.groupId}`);
59
+ let zkpRes;
60
+ try {
61
+ const isCircuitSupported = opts.supportedCircuits.includes(proofReq.circuitId);
62
+ if (!isCircuitSupported) {
63
+ if (proofReq.optional) {
64
+ continue;
65
+ }
66
+ throw new Error(`Circuit ${proofReq.circuitId} is not allowed`);
67
67
  }
68
- const combinedQuery = combinedQueryData.query;
69
- if (!groupedCredentialsCache.has(groupId)) {
70
- const credWithRevStatus = await proofService.findCredentialByProofQuery(to, combinedQueryData.query);
71
- if (!credWithRevStatus.cred) {
72
- throw new Error(`Credential not found for query ${JSON.stringify(combinedQuery)}`);
68
+ const query = proofReq.query;
69
+ const groupId = query.groupId;
70
+ const combinedQueryData = combinedQueries.get(groupId);
71
+ if (groupId) {
72
+ if (!combinedQueryData) {
73
+ throw new Error(`Invalid group id ${query.groupId}`);
74
+ }
75
+ const combinedQuery = combinedQueryData.query;
76
+ if (!groupedCredentialsCache.has(groupId)) {
77
+ const credWithRevStatus = await proofService.findCredentialByProofQuery(to, combinedQueryData.query);
78
+ if (!credWithRevStatus.cred) {
79
+ if (proofReq.optional) {
80
+ continue;
81
+ }
82
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY +
83
+ `${JSON.stringify(combinedQuery)}`);
84
+ }
85
+ groupedCredentialsCache.set(groupId, credWithRevStatus);
73
86
  }
74
- groupedCredentialsCache.set(groupId, credWithRevStatus);
75
87
  }
88
+ const credWithRevStatus = groupedCredentialsCache.get(groupId);
89
+ zkpRes = await proofService.generateProof(proofReq, to, {
90
+ verifierDid: from,
91
+ challenge: opts.challenge,
92
+ skipRevocation: Boolean(query.skipClaimRevocationCheck),
93
+ credential: credWithRevStatus?.cred,
94
+ credentialRevocationStatus: credWithRevStatus?.revStatus,
95
+ linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
96
+ });
97
+ }
98
+ catch (error) {
99
+ const expectedErrors = [
100
+ verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_IDENTITY_OR_PROFILE,
101
+ verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_NO_CREDENTIAL_SATISFIED_QUERY,
102
+ verifiable_1.VerifiableConstants.ERRORS.CREDENTIAL_WALLET_ALL_CREDENTIALS_ARE_REVOKED
103
+ ];
104
+ // handle only errors in case credential is not found and it is optional proof request - otherwise throw
105
+ if (error instanceof Error &&
106
+ (expectedErrors.includes(error.message) ||
107
+ error.message.includes(verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY)) &&
108
+ proofReq.optional) {
109
+ continue;
110
+ }
111
+ throw error;
76
112
  }
77
- const credWithRevStatus = groupedCredentialsCache.get(groupId);
78
- const zkpRes = await proofService.generateProof(proofReq, to, {
79
- verifierDid: from,
80
- challenge: opts.challenge,
81
- skipRevocation: Boolean(query.skipClaimRevocationCheck),
82
- credential: credWithRevStatus?.cred,
83
- credentialRevocationStatus: credWithRevStatus?.revStatus,
84
- linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
85
- });
86
113
  zkpResponses.push(zkpRes);
87
114
  }
88
115
  return zkpResponses;
@@ -229,7 +229,7 @@ class IdentityWallet {
229
229
  let credential;
230
230
  const ethSigner = opts.ethSigner;
231
231
  if (opts.createBjjCredential && !ethSigner) {
232
- throw new Error('Ethereum signer is required to create Ethereum identities in order to transit state');
232
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_SIGNER_IS_REQUIRED);
233
233
  }
234
234
  const currentState = js_merkletree_1.ZERO_HASH; // In Ethereum identities we don't have an initial state with the auth credential
235
235
  const didType = (0, js_iden3_core_1.buildDIDType)(opts.method || js_iden3_core_1.DidMethod.Iden3, opts.blockchain || js_iden3_core_1.Blockchain.Polygon, opts.networkId || js_iden3_core_1.NetworkId.Amoy);
@@ -267,7 +267,7 @@ class IdentityWallet {
267
267
  }
268
268
  const profile = await this._storage.identity.getProfileById(did.string());
269
269
  if (!profile) {
270
- throw new Error('profile or identity not found');
270
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_PROFILE_OR_IDENTITY_NOT_FOUND);
271
271
  }
272
272
  return { nonce: profile.nonce, genesisDID: js_iden3_core_1.DID.parse(profile.genesisIdentifier) };
273
273
  }
@@ -277,7 +277,7 @@ class IdentityWallet {
277
277
  const identityProfiles = await this._storage.identity.getProfilesByGenesisIdentifier(did.string());
278
278
  const existingProfile = identityProfiles.find((p) => p.nonce == nonce || p.verifier == verifier);
279
279
  if (existingProfile) {
280
- throw new Error('profile with given nonce or verifier already exists');
280
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_PROFILE_ALREADY_EXISTS);
281
281
  }
282
282
  await this._storage.identity.saveProfile({
283
283
  id: profileDID.string(),
@@ -426,11 +426,11 @@ class IdentityWallet {
426
426
  const coreClaimHash = js_crypto_1.poseidon.hash([hi, hv]);
427
427
  const signature = await this.signChallenge(coreClaimHash, issuerAuthBJJCredential);
428
428
  if (!issuerAuthBJJCredential.proof) {
429
- throw new Error('issuer auth credential must have proof');
429
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_ISSUER_AUTH_BJJ_CRED_MUST_HAVE_ANY_PROOF);
430
430
  }
431
431
  const mtpAuthBJJProof = issuerAuthBJJCredential.getIden3SparseMerkleTreeProof();
432
432
  if (!mtpAuthBJJProof) {
433
- throw new Error('mtp is required for auth bjj key to issue new credentials');
433
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_ISSUER_AUTH_BJJ_CRED_MUST_HAVE_MTP_PROOF);
434
434
  }
435
435
  const sigProof = new verifiable_1.BJJSignatureProof2021({
436
436
  issuerData: {
@@ -470,7 +470,7 @@ class IdentityWallet {
470
470
  const issuerTree = await this.getDIDTreeModel(issuerDID);
471
471
  const coreClaim = await this.getCoreClaimFromCredential(credential);
472
472
  if (!coreClaim) {
473
- throw new Error('credential must have coreClaim representation in proofs');
473
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_REQUIRED_IN_ANY_PROOF);
474
474
  }
475
475
  const nonce = coreClaim.getRevocationNonce();
476
476
  await issuerTree.revocationTree.add(nonce, BigInt(0));
@@ -493,7 +493,7 @@ class IdentityWallet {
493
493
  // credential must have a bjj signature proof
494
494
  const coreClaim = credential.getCoreClaimFromProof(verifiable_1.ProofType.BJJSignature);
495
495
  if (!coreClaim) {
496
- throw new Error('credential must have coreClaim representation in the signature proof');
496
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_REQUIRED_IN_SIG_PROOF);
497
497
  }
498
498
  await this._storage.mt.addToMerkleTree(issuerDID.string(), storage_1.MerkleTreeType.Claims, coreClaim.hIndex(), coreClaim.hValue());
499
499
  }
@@ -525,7 +525,7 @@ class IdentityWallet {
525
525
  const coreClaim = credential.getCoreClaimFromProof(verifiable_1.ProofType.BJJSignature) ||
526
526
  (await credential.toCoreClaim(opts));
527
527
  if (!coreClaim) {
528
- throw new Error('credential must have coreClaim representation in the signature proof');
528
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_REQUIRED_IN_SIG_PROOF);
529
529
  }
530
530
  const mtpWithProof = await this.generateCoreClaimMtp(issuerDID, coreClaim, treeState);
531
531
  const mtpProof = new verifiable_1.Iden3SparseMerkleTreeProof({
@@ -595,10 +595,10 @@ class IdentityWallet {
595
595
  if (coreClaimFromMtpProof &&
596
596
  coreClaimFromSigProof &&
597
597
  coreClaimFromMtpProof.hex() !== coreClaimFromSigProof.hex()) {
598
- throw new Error('core claim representations is set in both proofs but they are not equal');
598
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_MISMATCH);
599
599
  }
600
600
  if (!coreClaimFromMtpProof && !coreClaimFromSigProof) {
601
- throw new Error('core claim is not set in credential proofs');
601
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_IS_NOT_SET);
602
602
  }
603
603
  //eslint-disable-next-line @typescript-eslint/no-non-null-assertion
604
604
  const coreClaim = coreClaimFromMtpProof ?? coreClaimFromSigProof;
@@ -607,7 +607,7 @@ class IdentityWallet {
607
607
  async findOwnedCredentialsByDID(did, query) {
608
608
  const credentials = await this._credentialWallet.findByQuery(query);
609
609
  if (!credentials.length) {
610
- throw new Error(`no credential satisfied query`);
610
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_NO_CREDENTIAL_SATISFIED_QUERY);
611
611
  }
612
612
  const { genesisDID } = await this.getGenesisDIDMetadata(did);
613
613
  const profiles = await this.getProfilesByDID(genesisDID);
@@ -647,7 +647,7 @@ class IdentityWallet {
647
647
  let txId;
648
648
  if (!isEthIdentity) {
649
649
  if (!prover) {
650
- throw new Error('prover is required to generate proofs for non ethereum identities');
650
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_PROVER_IS_REQUIRED);
651
651
  }
652
652
  // generate the proof
653
653
  const authInfo = await this._inputsGenerator.prepareAuthBJJCredential(did, oldTreeState);
@@ -76,7 +76,8 @@ class ProofService {
76
76
  credentialWithRevStatus = { cred: opts.credential, revStatus };
77
77
  }
78
78
  if (!credentialWithRevStatus.cred) {
79
- throw new Error(`credential not found for query ${JSON.stringify(proofReq.query)}`);
79
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY +
80
+ ` ${JSON.stringify(proofReq.query)}`);
80
81
  }
81
82
  const credentialCoreClaim = await this._identityWallet.getCoreClaimFromCredential(credentialWithRevStatus.cred);
82
83
  const { nonce: authProfileNonce, genesisDID } = await this._identityWallet.getGenesisDIDMetadata(identifier);
@@ -88,11 +89,11 @@ class ProofService {
88
89
  const subjectDID = js_iden3_core_1.DID.parse(preparedCredential.credential.credentialSubject['id']);
89
90
  const { nonce: credentialSubjectProfileNonce, genesisDID: subjectGenesisDID } = await this._identityWallet.getGenesisDIDMetadata(subjectDID);
90
91
  if (subjectGenesisDID.string() !== genesisDID.string()) {
91
- throw new Error('subject and auth profiles are not derived from the same did');
92
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_PROFILE_GENESIS_DID_MISMATCH);
92
93
  }
93
94
  const propertiesMetadata = (0, common_1.parseCredentialSubject)(proofReq.query.credentialSubject);
94
95
  if (!propertiesMetadata.length) {
95
- throw new Error('no queries in zkp request');
96
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_NO_QUERIES_IN_ZKP_REQUEST);
96
97
  }
97
98
  const mtPosition = preparedCredential.credentialCoreClaim.getMerklizedPosition();
98
99
  let mk;
@@ -276,7 +277,7 @@ class ProofService {
276
277
  async findCredentialByProofQuery(did, query) {
277
278
  const credentials = await this._identityWallet.findOwnedCredentialsByDID(did, query);
278
279
  if (!credentials.length) {
279
- throw new Error(`no credentials belong to did or its profiles`);
280
+ throw new Error(verifiable_1.VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_IDENTITY_OR_PROFILE);
280
281
  }
281
282
  // For EQ / IN / NIN / LT / GT operations selective if credential satisfies query - we can get any.
282
283
  // TODO: choose credential for selective credentials
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fieldValueFromVerifiablePresentation = exports.validateEmptyCredentialSubjectNoopNativeSupport = exports.validateDisclosureNativeSDSupport = exports.validateDisclosureV2Circuit = exports.validateOperators = exports.validateEmptyCredentialSubjectV2Circuit = exports.verifyFieldValueInclusionNativeExistsSupport = exports.verifyFieldValueInclusionV2 = exports.checkCircuitOperator = exports.checkCircuitQueriesLength = exports.checkQueryRequest = void 0;
3
+ exports.calculateMultiRequestId = exports.calculateRequestId = exports.calculateGroupId = exports.fieldValueFromVerifiablePresentation = exports.validateEmptyCredentialSubjectNoopNativeSupport = exports.validateDisclosureNativeSDSupport = exports.validateDisclosureV2Circuit = exports.validateOperators = exports.validateEmptyCredentialSubjectV2Circuit = exports.verifyFieldValueInclusionNativeExistsSupport = exports.verifyFieldValueInclusionV2 = exports.checkCircuitOperator = exports.checkCircuitQueriesLength = exports.checkQueryRequest = void 0;
4
4
  const js_iden3_core_1 = require("@iden3/js-iden3-core");
5
5
  const js_jsonld_merklization_1 = require("@iden3/js-jsonld-merklization");
6
6
  const utils_1 = require("../../utils");
7
7
  const comparer_1 = require("../../circuits/comparer");
8
8
  const verifiable_1 = require("../../verifiable");
9
9
  const provers_1 = require("../provers");
10
+ const ethers_1 = require("ethers");
10
11
  const defaultProofGenerationDelayOpts = 24 * 60 * 60 * 1000; // 24 hours
11
12
  async function checkQueryRequest(query, queriesMetadata, ldContext, outputs, circuitId, schemaLoader, opts) {
12
13
  // validate issuer
@@ -211,3 +212,26 @@ const fieldValueFromVerifiablePresentation = async (fieldName, verifiablePresent
211
212
  return await value.mtEntry();
212
213
  };
213
214
  exports.fieldValueFromVerifiablePresentation = fieldValueFromVerifiablePresentation;
215
+ function calculateGroupId(requestIds) {
216
+ const types = Array(requestIds.length).fill('uint256');
217
+ const groupID = BigInt(ethers_1.ethers.keccak256(ethers_1.ethers.solidityPacked(types, requestIds))) &
218
+ // It should fit in a field number in the circuit (max 253 bits). With this we truncate to 252 bits for the group ID
219
+ BigInt('0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
220
+ return groupID;
221
+ }
222
+ exports.calculateGroupId = calculateGroupId;
223
+ function calculateRequestId(requestParams, creatorAddress) {
224
+ // 0x0000000000000000FFFF...FF. Reserved first 8 bytes for the request Id type and future use
225
+ // 0x00010000000000000000...00. First 2 bytes for the request Id type
226
+ // - 0x0000... for old request Ids with uint64
227
+ // - 0x0001... for new request Ids with uint256
228
+ const requestId = (BigInt(ethers_1.ethers.keccak256(ethers_1.ethers.solidityPacked(['bytes', 'address'], [requestParams, creatorAddress]))) &
229
+ BigInt('0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')) +
230
+ BigInt('0x0001000000000000000000000000000000000000000000000000000000000000');
231
+ return requestId;
232
+ }
233
+ exports.calculateRequestId = calculateRequestId;
234
+ function calculateMultiRequestId(requestIds, groupIds, creatorAddress) {
235
+ return BigInt(ethers_1.ethers.keccak256(ethers_1.ethers.solidityPacked(['uint256[]', 'uint256[]', 'address'], [requestIds, groupIds, creatorAddress])));
236
+ }
237
+ exports.calculateMultiRequestId = calculateMultiRequestId;
@@ -44,6 +44,7 @@ class JsonSchemaValidator {
44
44
  validator = ajv ?? defaultJSONSchemaValidator;
45
45
  if (validator.formats && !Object.keys(validator.formats).length) {
46
46
  (0, ajv_formats_1.default)(validator);
47
+ addCustomFormats(validator);
47
48
  }
48
49
  const validate = (schema.$id ? validator.getSchema(schema.$id) : undefined) || validator.compile(schema);
49
50
  const valid = validate(data);
@@ -55,3 +56,13 @@ class JsonSchemaValidator {
55
56
  }
56
57
  }
57
58
  exports.JsonSchemaValidator = JsonSchemaValidator;
59
+ function addCustomFormats(validator) {
60
+ validator.addFormat('positive-integer', {
61
+ type: 'string',
62
+ validate: (positiveIntegerStr) => /^[1-9]\d*$/.test(positiveIntegerStr)
63
+ });
64
+ validator.addFormat('non-negative-integer', {
65
+ type: 'string',
66
+ validate: (nonNegativeIntegerStr) => /^(0|[1-9]\d*)$/.test(nonNegativeIntegerStr)
67
+ });
68
+ }
@@ -8,7 +8,26 @@ exports.VerifiableConstants = Object.freeze({
8
8
  CONTEXT_TYPE_IS_EMPTY: 'ctxType is empty',
9
9
  // ErrStateNotFound issuer state is genesis state.
10
10
  IDENTITY_DOES_NOT_EXIST: 'Identity does not exist',
11
- NO_AUTH_CRED_FOUND: 'no auth credentials found'
11
+ NO_AUTH_CRED_FOUND: 'no auth credentials found',
12
+ // identity wallet
13
+ ID_WALLET_NO_CREDENTIAL_SATISFIED_QUERY: 'no credential satisfied query',
14
+ ID_WALLET_SIGNER_IS_REQUIRED: 'Ethereum signer is required to create Ethereum identities in order to transit state',
15
+ ID_WALLET_PROVER_IS_REQUIRED: 'prover is required to generate proofs for non ethereum identities',
16
+ ID_WALLET_CORE_CLAIM_REQUIRED_IN_SIG_PROOF: 'credential must have coreClaim representation in the signature proof',
17
+ ID_WALLET_CORE_CLAIM_REQUIRED_IN_ANY_PROOF: 'credential must have coreClaim representation in proofs',
18
+ ID_WALLET_CORE_CLAIM_MISMATCH: 'core claim representations is set in both proofs but they are not equal',
19
+ ID_WALLET_CORE_CLAIM_IS_NOT_SET: 'core claim is not set in credential proofs',
20
+ ID_WALLET_PROFILE_OR_IDENTITY_NOT_FOUND: 'profile or identity not found',
21
+ ID_WALLET_PROFILE_ALREADY_EXISTS: 'profile with given nonce or verifier already exists',
22
+ ID_WALLET_ISSUER_AUTH_BJJ_CRED_MUST_HAVE_ANY_PROOF: 'issuer auth credential must have proof',
23
+ ID_WALLET_ISSUER_AUTH_BJJ_CRED_MUST_HAVE_MTP_PROOF: 'mtp is required for auth bjj key to issue new credentials',
24
+ // proof service
25
+ PROOF_SERVICE_NO_CREDENTIAL_FOR_IDENTITY_OR_PROFILE: 'no credentials belong to did or its profiles',
26
+ PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY: 'credential not found for query',
27
+ PROOF_SERVICE_PROFILE_GENESIS_DID_MISMATCH: 'subject and auth profiles are not derived from the same did',
28
+ PROOF_SERVICE_NO_QUERIES_IN_ZKP_REQUEST: 'no queries in zkp request',
29
+ // credential wallet
30
+ CREDENTIAL_WALLET_ALL_CREDENTIALS_ARE_REVOKED: 'all claims are revoked'
12
31
  },
13
32
  CREDENTIAL_TYPE: {
14
33
  // VerifiableCredential is a W3C verifiable credential type
@@ -47,12 +47,12 @@ const createVerifiablePresentation = (context, tp, credential, queries) => {
47
47
  }
48
48
  const skeleton = {
49
49
  '@context': baseContext,
50
- '@type': constants_1.VerifiableConstants.CREDENTIAL_TYPE.W3C_VERIFIABLE_PRESENTATION,
50
+ type: constants_1.VerifiableConstants.CREDENTIAL_TYPE.W3C_VERIFIABLE_PRESENTATION,
51
51
  verifiableCredential: {
52
52
  '@context': ldContext,
53
- '@type': vcTypes,
53
+ type: vcTypes,
54
54
  credentialSubject: {
55
- '@type': tp
55
+ type: tp
56
56
  }
57
57
  }
58
58
  };
@@ -170,6 +170,6 @@ export class CredentialWallet {
170
170
  }
171
171
  return { cred, revStatus };
172
172
  }
173
- throw new Error('all claims are revoked');
173
+ throw new Error(VerifiableConstants.ERRORS.CREDENTIAL_WALLET_ALL_CREDENTIALS_ARE_REVOKED);
174
174
  }
175
175
  }
@@ -1,6 +1,7 @@
1
1
  import { getRandomBytes } from '@iden3/js-crypto';
2
2
  import { AuthMethod } from '../types';
3
3
  import { mergeObjects } from '../../utils';
4
+ import { VerifiableConstants } from '../../verifiable';
4
5
  import { getUnixTimestamp } from '@iden3/js-iden3-core';
5
6
  import { AcceptJwsAlgorithms, defaultAcceptProfile, MediaType } from '../constants';
6
7
  import { ethers } from 'ethers';
@@ -52,34 +53,60 @@ export const processZeroKnowledgeProofRequests = async (to, requests, from, proo
52
53
  const groupedCredentialsCache = new Map();
53
54
  const zkpResponses = [];
54
55
  for (const proofReq of requestScope) {
55
- if (!opts.supportedCircuits.includes(proofReq.circuitId)) {
56
- throw new Error(`Circuit ${proofReq.circuitId} is not allowed`);
57
- }
58
- const query = proofReq.query;
59
- const groupId = query.groupId;
60
- const combinedQueryData = combinedQueries.get(groupId);
61
- if (groupId) {
62
- if (!combinedQueryData) {
63
- throw new Error(`Invalid group id ${query.groupId}`);
56
+ let zkpRes;
57
+ try {
58
+ const isCircuitSupported = opts.supportedCircuits.includes(proofReq.circuitId);
59
+ if (!isCircuitSupported) {
60
+ if (proofReq.optional) {
61
+ continue;
62
+ }
63
+ throw new Error(`Circuit ${proofReq.circuitId} is not allowed`);
64
64
  }
65
- const combinedQuery = combinedQueryData.query;
66
- if (!groupedCredentialsCache.has(groupId)) {
67
- const credWithRevStatus = await proofService.findCredentialByProofQuery(to, combinedQueryData.query);
68
- if (!credWithRevStatus.cred) {
69
- throw new Error(`Credential not found for query ${JSON.stringify(combinedQuery)}`);
65
+ const query = proofReq.query;
66
+ const groupId = query.groupId;
67
+ const combinedQueryData = combinedQueries.get(groupId);
68
+ if (groupId) {
69
+ if (!combinedQueryData) {
70
+ throw new Error(`Invalid group id ${query.groupId}`);
71
+ }
72
+ const combinedQuery = combinedQueryData.query;
73
+ if (!groupedCredentialsCache.has(groupId)) {
74
+ const credWithRevStatus = await proofService.findCredentialByProofQuery(to, combinedQueryData.query);
75
+ if (!credWithRevStatus.cred) {
76
+ if (proofReq.optional) {
77
+ continue;
78
+ }
79
+ throw new Error(VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY +
80
+ `${JSON.stringify(combinedQuery)}`);
81
+ }
82
+ groupedCredentialsCache.set(groupId, credWithRevStatus);
70
83
  }
71
- groupedCredentialsCache.set(groupId, credWithRevStatus);
72
84
  }
85
+ const credWithRevStatus = groupedCredentialsCache.get(groupId);
86
+ zkpRes = await proofService.generateProof(proofReq, to, {
87
+ verifierDid: from,
88
+ challenge: opts.challenge,
89
+ skipRevocation: Boolean(query.skipClaimRevocationCheck),
90
+ credential: credWithRevStatus?.cred,
91
+ credentialRevocationStatus: credWithRevStatus?.revStatus,
92
+ linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
93
+ });
94
+ }
95
+ catch (error) {
96
+ const expectedErrors = [
97
+ VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_IDENTITY_OR_PROFILE,
98
+ VerifiableConstants.ERRORS.ID_WALLET_NO_CREDENTIAL_SATISFIED_QUERY,
99
+ VerifiableConstants.ERRORS.CREDENTIAL_WALLET_ALL_CREDENTIALS_ARE_REVOKED
100
+ ];
101
+ // handle only errors in case credential is not found and it is optional proof request - otherwise throw
102
+ if (error instanceof Error &&
103
+ (expectedErrors.includes(error.message) ||
104
+ error.message.includes(VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY)) &&
105
+ proofReq.optional) {
106
+ continue;
107
+ }
108
+ throw error;
73
109
  }
74
- const credWithRevStatus = groupedCredentialsCache.get(groupId);
75
- const zkpRes = await proofService.generateProof(proofReq, to, {
76
- verifierDid: from,
77
- challenge: opts.challenge,
78
- skipRevocation: Boolean(query.skipClaimRevocationCheck),
79
- credential: credWithRevStatus?.cred,
80
- credentialRevocationStatus: credWithRevStatus?.revStatus,
81
- linkNonce: combinedQueryData?.linkNonce ? BigInt(combinedQueryData.linkNonce) : undefined
82
- });
83
110
  zkpResponses.push(zkpRes);
84
111
  }
85
112
  return zkpResponses;
@@ -203,7 +203,7 @@ export class IdentityWallet {
203
203
  let credential;
204
204
  const ethSigner = opts.ethSigner;
205
205
  if (opts.createBjjCredential && !ethSigner) {
206
- throw new Error('Ethereum signer is required to create Ethereum identities in order to transit state');
206
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_SIGNER_IS_REQUIRED);
207
207
  }
208
208
  const currentState = ZERO_HASH; // In Ethereum identities we don't have an initial state with the auth credential
209
209
  const didType = buildDIDType(opts.method || DidMethod.Iden3, opts.blockchain || Blockchain.Polygon, opts.networkId || NetworkId.Amoy);
@@ -241,7 +241,7 @@ export class IdentityWallet {
241
241
  }
242
242
  const profile = await this._storage.identity.getProfileById(did.string());
243
243
  if (!profile) {
244
- throw new Error('profile or identity not found');
244
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_PROFILE_OR_IDENTITY_NOT_FOUND);
245
245
  }
246
246
  return { nonce: profile.nonce, genesisDID: DID.parse(profile.genesisIdentifier) };
247
247
  }
@@ -251,7 +251,7 @@ export class IdentityWallet {
251
251
  const identityProfiles = await this._storage.identity.getProfilesByGenesisIdentifier(did.string());
252
252
  const existingProfile = identityProfiles.find((p) => p.nonce == nonce || p.verifier == verifier);
253
253
  if (existingProfile) {
254
- throw new Error('profile with given nonce or verifier already exists');
254
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_PROFILE_ALREADY_EXISTS);
255
255
  }
256
256
  await this._storage.identity.saveProfile({
257
257
  id: profileDID.string(),
@@ -400,11 +400,11 @@ export class IdentityWallet {
400
400
  const coreClaimHash = poseidon.hash([hi, hv]);
401
401
  const signature = await this.signChallenge(coreClaimHash, issuerAuthBJJCredential);
402
402
  if (!issuerAuthBJJCredential.proof) {
403
- throw new Error('issuer auth credential must have proof');
403
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_ISSUER_AUTH_BJJ_CRED_MUST_HAVE_ANY_PROOF);
404
404
  }
405
405
  const mtpAuthBJJProof = issuerAuthBJJCredential.getIden3SparseMerkleTreeProof();
406
406
  if (!mtpAuthBJJProof) {
407
- throw new Error('mtp is required for auth bjj key to issue new credentials');
407
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_ISSUER_AUTH_BJJ_CRED_MUST_HAVE_MTP_PROOF);
408
408
  }
409
409
  const sigProof = new BJJSignatureProof2021({
410
410
  issuerData: {
@@ -444,7 +444,7 @@ export class IdentityWallet {
444
444
  const issuerTree = await this.getDIDTreeModel(issuerDID);
445
445
  const coreClaim = await this.getCoreClaimFromCredential(credential);
446
446
  if (!coreClaim) {
447
- throw new Error('credential must have coreClaim representation in proofs');
447
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_REQUIRED_IN_ANY_PROOF);
448
448
  }
449
449
  const nonce = coreClaim.getRevocationNonce();
450
450
  await issuerTree.revocationTree.add(nonce, BigInt(0));
@@ -467,7 +467,7 @@ export class IdentityWallet {
467
467
  // credential must have a bjj signature proof
468
468
  const coreClaim = credential.getCoreClaimFromProof(ProofType.BJJSignature);
469
469
  if (!coreClaim) {
470
- throw new Error('credential must have coreClaim representation in the signature proof');
470
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_REQUIRED_IN_SIG_PROOF);
471
471
  }
472
472
  await this._storage.mt.addToMerkleTree(issuerDID.string(), MerkleTreeType.Claims, coreClaim.hIndex(), coreClaim.hValue());
473
473
  }
@@ -499,7 +499,7 @@ export class IdentityWallet {
499
499
  const coreClaim = credential.getCoreClaimFromProof(ProofType.BJJSignature) ||
500
500
  (await credential.toCoreClaim(opts));
501
501
  if (!coreClaim) {
502
- throw new Error('credential must have coreClaim representation in the signature proof');
502
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_REQUIRED_IN_SIG_PROOF);
503
503
  }
504
504
  const mtpWithProof = await this.generateCoreClaimMtp(issuerDID, coreClaim, treeState);
505
505
  const mtpProof = new Iden3SparseMerkleTreeProof({
@@ -569,10 +569,10 @@ export class IdentityWallet {
569
569
  if (coreClaimFromMtpProof &&
570
570
  coreClaimFromSigProof &&
571
571
  coreClaimFromMtpProof.hex() !== coreClaimFromSigProof.hex()) {
572
- throw new Error('core claim representations is set in both proofs but they are not equal');
572
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_MISMATCH);
573
573
  }
574
574
  if (!coreClaimFromMtpProof && !coreClaimFromSigProof) {
575
- throw new Error('core claim is not set in credential proofs');
575
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_IS_NOT_SET);
576
576
  }
577
577
  //eslint-disable-next-line @typescript-eslint/no-non-null-assertion
578
578
  const coreClaim = coreClaimFromMtpProof ?? coreClaimFromSigProof;
@@ -581,7 +581,7 @@ export class IdentityWallet {
581
581
  async findOwnedCredentialsByDID(did, query) {
582
582
  const credentials = await this._credentialWallet.findByQuery(query);
583
583
  if (!credentials.length) {
584
- throw new Error(`no credential satisfied query`);
584
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_NO_CREDENTIAL_SATISFIED_QUERY);
585
585
  }
586
586
  const { genesisDID } = await this.getGenesisDIDMetadata(did);
587
587
  const profiles = await this.getProfilesByDID(genesisDID);
@@ -621,7 +621,7 @@ export class IdentityWallet {
621
621
  let txId;
622
622
  if (!isEthIdentity) {
623
623
  if (!prover) {
624
- throw new Error('prover is required to generate proofs for non ethereum identities');
624
+ throw new Error(VerifiableConstants.ERRORS.ID_WALLET_PROVER_IS_REQUIRED);
625
625
  }
626
626
  // generate the proof
627
627
  const authInfo = await this._inputsGenerator.prepareAuthBJJCredential(did, oldTreeState);
@@ -1,6 +1,6 @@
1
1
  import { BytesHelper, DID, MerklizedRootPosition, getDateFromUnixTimestamp } from '@iden3/js-iden3-core';
2
2
  import { AuthV2Inputs, AuthV2PubSignals, CircuitId, Operators, Query, ValueProof } from '../circuits';
3
- import { createVerifiablePresentation } from '../verifiable';
3
+ import { createVerifiablePresentation, VerifiableConstants } from '../verifiable';
4
4
  import { parseCredentialSubject, parseQueryMetadata, toGISTProof, transformQueryValueToBigInts } from './common';
5
5
  import { NativeProver } from './provers/prover';
6
6
  import { getDocumentLoader } from '@iden3/js-jsonld-merklization';
@@ -73,7 +73,8 @@ export class ProofService {
73
73
  credentialWithRevStatus = { cred: opts.credential, revStatus };
74
74
  }
75
75
  if (!credentialWithRevStatus.cred) {
76
- throw new Error(`credential not found for query ${JSON.stringify(proofReq.query)}`);
76
+ throw new Error(VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_QUERY +
77
+ ` ${JSON.stringify(proofReq.query)}`);
77
78
  }
78
79
  const credentialCoreClaim = await this._identityWallet.getCoreClaimFromCredential(credentialWithRevStatus.cred);
79
80
  const { nonce: authProfileNonce, genesisDID } = await this._identityWallet.getGenesisDIDMetadata(identifier);
@@ -85,11 +86,11 @@ export class ProofService {
85
86
  const subjectDID = DID.parse(preparedCredential.credential.credentialSubject['id']);
86
87
  const { nonce: credentialSubjectProfileNonce, genesisDID: subjectGenesisDID } = await this._identityWallet.getGenesisDIDMetadata(subjectDID);
87
88
  if (subjectGenesisDID.string() !== genesisDID.string()) {
88
- throw new Error('subject and auth profiles are not derived from the same did');
89
+ throw new Error(VerifiableConstants.ERRORS.PROOF_SERVICE_PROFILE_GENESIS_DID_MISMATCH);
89
90
  }
90
91
  const propertiesMetadata = parseCredentialSubject(proofReq.query.credentialSubject);
91
92
  if (!propertiesMetadata.length) {
92
- throw new Error('no queries in zkp request');
93
+ throw new Error(VerifiableConstants.ERRORS.PROOF_SERVICE_NO_QUERIES_IN_ZKP_REQUEST);
93
94
  }
94
95
  const mtPosition = preparedCredential.credentialCoreClaim.getMerklizedPosition();
95
96
  let mk;
@@ -273,7 +274,7 @@ export class ProofService {
273
274
  async findCredentialByProofQuery(did, query) {
274
275
  const credentials = await this._identityWallet.findOwnedCredentialsByDID(did, query);
275
276
  if (!credentials.length) {
276
- throw new Error(`no credentials belong to did or its profiles`);
277
+ throw new Error(VerifiableConstants.ERRORS.PROOF_SERVICE_NO_CREDENTIAL_FOR_IDENTITY_OR_PROFILE);
277
278
  }
278
279
  // For EQ / IN / NIN / LT / GT operations selective if credential satisfies query - we can get any.
279
280
  // TODO: choose credential for selective credentials
@@ -4,6 +4,7 @@ import { byteEncoder } from '../../utils';
4
4
  import { getOperatorNameByValue, Operators, QueryOperators } from '../../circuits/comparer';
5
5
  import { calculateCoreSchemaHash, VerifiableConstants } from '../../verifiable';
6
6
  import { circuitValidator } from '../provers';
7
+ import { ethers } from 'ethers';
7
8
  const defaultProofGenerationDelayOpts = 24 * 60 * 60 * 1000; // 24 hours
8
9
  export async function checkQueryRequest(query, queriesMetadata, ldContext, outputs, circuitId, schemaLoader, opts) {
9
10
  // validate issuer
@@ -197,3 +198,23 @@ export const fieldValueFromVerifiablePresentation = async (fieldName, verifiable
197
198
  }
198
199
  return await value.mtEntry();
199
200
  };
201
+ export function calculateGroupId(requestIds) {
202
+ const types = Array(requestIds.length).fill('uint256');
203
+ const groupID = BigInt(ethers.keccak256(ethers.solidityPacked(types, requestIds))) &
204
+ // It should fit in a field number in the circuit (max 253 bits). With this we truncate to 252 bits for the group ID
205
+ BigInt('0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
206
+ return groupID;
207
+ }
208
+ export function calculateRequestId(requestParams, creatorAddress) {
209
+ // 0x0000000000000000FFFF...FF. Reserved first 8 bytes for the request Id type and future use
210
+ // 0x00010000000000000000...00. First 2 bytes for the request Id type
211
+ // - 0x0000... for old request Ids with uint64
212
+ // - 0x0001... for new request Ids with uint256
213
+ const requestId = (BigInt(ethers.keccak256(ethers.solidityPacked(['bytes', 'address'], [requestParams, creatorAddress]))) &
214
+ BigInt('0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')) +
215
+ BigInt('0x0001000000000000000000000000000000000000000000000000000000000000');
216
+ return requestId;
217
+ }
218
+ export function calculateMultiRequestId(requestIds, groupIds, creatorAddress) {
219
+ return BigInt(ethers.keccak256(ethers.solidityPacked(['uint256[]', 'uint256[]', 'address'], [requestIds, groupIds, creatorAddress])));
220
+ }
@@ -38,6 +38,7 @@ export class JsonSchemaValidator {
38
38
  validator = ajv ?? defaultJSONSchemaValidator;
39
39
  if (validator.formats && !Object.keys(validator.formats).length) {
40
40
  addFormats(validator);
41
+ addCustomFormats(validator);
41
42
  }
42
43
  const validate = (schema.$id ? validator.getSchema(schema.$id) : undefined) || validator.compile(schema);
43
44
  const valid = validate(data);
@@ -48,3 +49,13 @@ export class JsonSchemaValidator {
48
49
  return true;
49
50
  }
50
51
  }
52
+ function addCustomFormats(validator) {
53
+ validator.addFormat('positive-integer', {
54
+ type: 'string',
55
+ validate: (positiveIntegerStr) => /^[1-9]\d*$/.test(positiveIntegerStr)
56
+ });
57
+ validator.addFormat('non-negative-integer', {
58
+ type: 'string',
59
+ validate: (nonNegativeIntegerStr) => /^(0|[1-9]\d*)$/.test(nonNegativeIntegerStr)
60
+ });
61
+ }