@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.
- package/dist/browser/esm/index.js +200 -47
- package/dist/browser/esm/index.js.map +1 -1
- package/dist/browser/umd/index.js +202 -46
- package/dist/browser/umd/index.js.map +1 -1
- package/dist/node/cjs/credentials/credential-wallet.js +1 -1
- package/dist/node/cjs/iden3comm/handlers/common.js +51 -24
- package/dist/node/cjs/identity/identity-wallet.js +12 -12
- package/dist/node/cjs/proof/proof-service.js +5 -4
- package/dist/node/cjs/proof/verifiers/query.js +25 -1
- package/dist/node/cjs/schema-processor/json/validator.js +11 -0
- package/dist/node/cjs/verifiable/constants.js +20 -1
- package/dist/node/cjs/verifiable/presentation.js +3 -3
- package/dist/node/esm/credentials/credential-wallet.js +1 -1
- package/dist/node/esm/iden3comm/handlers/common.js +51 -24
- package/dist/node/esm/identity/identity-wallet.js +12 -12
- package/dist/node/esm/proof/proof-service.js +6 -5
- package/dist/node/esm/proof/verifiers/query.js +21 -0
- package/dist/node/esm/schema-processor/json/validator.js +11 -0
- package/dist/node/esm/verifiable/constants.js +20 -1
- package/dist/node/esm/verifiable/presentation.js +3 -3
- package/dist/types/iden3comm/types/protocol/auth.d.ts +2 -2
- package/dist/types/proof/verifiers/query.d.ts +3 -0
- package/dist/types/verifiable/constants.d.ts +16 -0
- package/package.json +1 -1
|
@@ -196,7 +196,7 @@ class CredentialWallet {
|
|
|
196
196
|
}
|
|
197
197
|
return { cred, revStatus };
|
|
198
198
|
}
|
|
199
|
-
throw new Error(
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
598
|
+
throw new Error(verifiable_1.VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_MISMATCH);
|
|
599
599
|
}
|
|
600
600
|
if (!coreClaimFromMtpProof && !coreClaimFromSigProof) {
|
|
601
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
50
|
+
type: constants_1.VerifiableConstants.CREDENTIAL_TYPE.W3C_VERIFIABLE_PRESENTATION,
|
|
51
51
|
verifiableCredential: {
|
|
52
52
|
'@context': ldContext,
|
|
53
|
-
|
|
53
|
+
type: vcTypes,
|
|
54
54
|
credentialSubject: {
|
|
55
|
-
|
|
55
|
+
type: tp
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
};
|
|
@@ -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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
572
|
+
throw new Error(VerifiableConstants.ERRORS.ID_WALLET_CORE_CLAIM_MISMATCH);
|
|
573
573
|
}
|
|
574
574
|
if (!coreClaimFromMtpProof && !coreClaimFromSigProof) {
|
|
575
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
+
}
|