diff --git a/package-lock.json b/package-lock.json index 7166112e..e4087e62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.0.0-beta.15", + "version": "1.0.0-beta.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@0xpolygonid/js-sdk", - "version": "1.0.0-beta.15", + "version": "1.0.0-beta.16", "license": "AGPL-3.0", "dependencies": { "@iden3/js-crypto": "1.0.0-beta.1", @@ -59,6 +59,9 @@ "ts-loader": "^9.4.1", "ts-node": "^10.9.1", "typescript": "^4.8.4" + }, + "engines": { + "node": ">=18.16.0" } }, "node_modules/@4c/fetch-mock": { diff --git a/package.json b/package.json index f4900eaa..bf8b9bcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.0.0-beta.15", + "version": "1.0.0-beta.16", "description": "SDK to work with Polygon ID", "main": "dist/cjs/index.js", "module": "dist/esm_esbuild/index.js", @@ -103,6 +103,6 @@ ] }, "engines": { - "node": ">=18.16.1" + "node": ">=18.16.0" } } diff --git a/src/credentials/credential-wallet.ts b/src/credentials/credential-wallet.ts index e5a35747..9deb87dc 100644 --- a/src/credentials/credential-wallet.ts +++ b/src/credentials/credential-wallet.ts @@ -39,7 +39,7 @@ export interface CredentialRequest { /** * Credential subject, usually contains claims and identifier */ - credentialSubject: { [key: string]: string | object | number }; + credentialSubject: { [key: string]: string | object | number | boolean }; /** * expiration time */ diff --git a/src/storage/filters/jsonQuery.ts b/src/storage/filters/jsonQuery.ts index 0f6eebf8..8bec58ad 100644 --- a/src/storage/filters/jsonQuery.ts +++ b/src/storage/filters/jsonQuery.ts @@ -52,32 +52,16 @@ const comparatorOptions: { [v in FilterOperatorMethod]: FilterOperatorFunction } * @param {string} path - given path * @param {*} [defaultValue=null] */ -export const resolvePath = (object: object, path: string, defaultValue = null) => - path.split('.').reduce((o, p) => (o ? o[p] : defaultValue), object); - -/** - * filter creation factory - * - * @param {string} path - given query path - * @param {*} operatorFunc - filter operation - * @param {*} value - filter value - * @param {*} [isReverseParams=false] - reversed params - */ -export const createFilter = (path: string, operatorFunc, value, isReverseParams = false) => { - if (!operatorFunc) { - throw new Error(SearchError.NotDefinedComparator); - } - return (credential: W3CCredential): boolean => { - const credentialPathValue = resolvePath(credential, path); - if (!credentialPathValue) { - return false; - // throw new Error(`Not found path - ${path} to credential`); +export const resolvePath = (object: object, path: string, defaultValue = null) => { + const pathParts = path.split('.'); + let o = object; + for (const part of pathParts) { + if (o === null || o === undefined) { + return defaultValue; } - if (isReverseParams) { - return operatorFunc(value, credentialPathValue); - } - return operatorFunc(credentialPathValue, value); - }; + o = o[part]; + } + return o; }; /** @@ -154,6 +138,11 @@ export const StandardJSONCredentialsQueryFilter = (query: ProofQuery): FilterQue case 'credentialSubject': { const reqFilters = Object.keys(queryValue).reduce((acc: FilterQuery[], fieldKey) => { const fieldParams = queryValue[fieldKey]; + if (typeof fieldParams === 'object' && Object.keys(fieldParams).length === 0) { + return acc.concat([ + new FilterQuery(`credentialSubject.${fieldKey}`, comparatorOptions.$noop, null) + ]); + } const res = Object.keys(fieldParams).map((comparator) => { const value = fieldParams[comparator]; const path = `credentialSubject.${fieldKey}`; diff --git a/src/storage/indexed-db/merkletree.ts b/src/storage/indexed-db/merkletree.ts index e71a9c59..dd46c7a5 100644 --- a/src/storage/indexed-db/merkletree.ts +++ b/src/storage/indexed-db/merkletree.ts @@ -33,7 +33,7 @@ export class MerkleTreeIndexedDBStorage implements IMerkleTreeStorage { */ constructor(private readonly _mtDepth: number) { this._merkleTreeMetaStore = createStore( - `${ MerkleTreeIndexedDBStorage.storageKeyMeta}-db`, + `${MerkleTreeIndexedDBStorage.storageKeyMeta}-db`, MerkleTreeIndexedDBStorage.storageKeyMeta ); this._bindingStore = createStore( diff --git a/src/verifiable/credential.ts b/src/verifiable/credential.ts index 6271ad9e..682452da 100644 --- a/src/verifiable/credential.ts +++ b/src/verifiable/credential.ts @@ -18,7 +18,7 @@ export class W3CCredential { type: string[]; expirationDate?: string; issuanceDate?: string; - credentialSubject: { [key: string]: object | string | number }; + credentialSubject: { [key: string]: object | string | number | boolean }; credentialStatus: CredentialStatus; issuer: string; credentialSchema: CredentialSchema; diff --git a/src/verifiable/presentation.ts b/src/verifiable/presentation.ts index ca220984..6d175e33 100644 --- a/src/verifiable/presentation.ts +++ b/src/verifiable/presentation.ts @@ -41,7 +41,7 @@ export const buildQueryPath = async ( export const createVerifiablePresentation = ( context: string, tp: string, - field: string, + path: string, value: unknown ): object => { const baseContext = [VerifiableConstants.JSONLD_SCHEMA.W3C_CREDENTIAL_2018]; @@ -53,6 +53,9 @@ export const createVerifiablePresentation = ( vcTypes.push(tp); } + const [first, ...rest] = path.split('.'); + const obj = rest.reduceRight((acc, key) => ({ [key]: acc }), value); + return { '@context': baseContext, '@type': VerifiableConstants.CREDENTIAL_TYPE.W3C_VERIFIABLE_PRESENTATION, @@ -61,7 +64,7 @@ export const createVerifiablePresentation = ( '@type': vcTypes, credentialSubject: { '@type': tp, - [field]: value + [first]: obj } } }; diff --git a/tests/credentials/credential-wallet.test.ts b/tests/credentials/credential-wallet.test.ts index 8df7417e..71934071 100644 --- a/tests/credentials/credential-wallet.test.ts +++ b/tests/credentials/credential-wallet.test.ts @@ -195,6 +195,24 @@ const credentialFlow = async (storage: IDataStorage) => { } }, expected: [] + }, + { + query: { + allowedIssuers: ['*'], + credentialSubject: { + countOfFines: {} + } + }, + expected: [cred4] + }, + { + query: { + allowedIssuers: ['*'], + credentialSubject: { + 'country.name': { $eq: 'Spain' } + } + }, + expected: [cred4] } ]; diff --git a/tests/credentials/mock.ts b/tests/credentials/mock.ts index e85fb5b3..e74f6a81 100644 --- a/tests/credentials/mock.ts +++ b/tests/credentials/mock.ts @@ -72,7 +72,11 @@ export const cred4 = createTestCredential({ credentialStatus: {}, issuer: 'issuer4', credentialSubject: { - countOfFines: 0 + countOfFines: 0, + country: { + name: 'Spain', + code: 'ES' + } }, expirationDate: '2023-11-11', issuanceDate: '2022-11-11' diff --git a/tests/handlers/auth.test.ts b/tests/handlers/auth.test.ts index 3af8b101..b4718b6f 100644 --- a/tests/handlers/auth.test.ts +++ b/tests/handlers/auth.test.ts @@ -267,7 +267,6 @@ describe('auth', () => { const msgBytes = byteEncoder.encode(JSON.stringify(authReq)); const authRes = await authHandler.handleAuthorizationRequestForGenesisDID(userDID, msgBytes); - const tokenStr = authRes.token; console.log(tokenStr); expect(tokenStr).to.be.a('string'); diff --git a/tests/proofs/common.ts b/tests/proofs/common.ts index 986711ec..1cbacc51 100644 --- a/tests/proofs/common.ts +++ b/tests/proofs/common.ts @@ -1,11 +1,12 @@ import { expect } from 'chai'; -import { CircuitId, ZeroKnowledgeProofRequest } from '../../src'; +import { CircuitId, IProofService, W3CCredential, ZeroKnowledgeProofRequest } from '../../src'; +import { DID } from '@iden3/js-iden3-core'; export async function checkVerifiablePresentation( type: string, - userDID, - cred, - proofService, + userDID: DID, + cred: W3CCredential, + proofService: IProofService, circuitId: CircuitId ) { const vpProofReq: ZeroKnowledgeProofRequest = { diff --git a/tests/proofs/sig.test.ts b/tests/proofs/sig.test.ts index 075957bc..c8c3125f 100644 --- a/tests/proofs/sig.test.ts +++ b/tests/proofs/sig.test.ts @@ -33,6 +33,8 @@ describe('sig proofs', () => { let dataStorage: IDataStorage; let proofService: ProofService; const rhsUrl = process.env.RHS_URL as string; + const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); + const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); const mockStateStorage: IStateStorage = { getLatestStateById: async () => { @@ -124,9 +126,6 @@ describe('sig proofs', () => { }); it('sigv2-non-merklized', async () => { - const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); - const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); - const { did: userDID, credential: cred } = await idWallet.createIdentity({ method: DidMethod.Iden3, blockchain: Blockchain.Polygon, @@ -205,9 +204,6 @@ describe('sig proofs', () => { }); it('sigv2-merklized', async () => { - const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); - const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); - const { did: userDID } = await idWallet.createIdentity({ method: DidMethod.Iden3, blockchain: Blockchain.Polygon, @@ -286,9 +282,6 @@ describe('sig proofs', () => { }); it('sigv2-merklized-query-array', async () => { - const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); - const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); - const { did: userDID } = await idWallet.createIdentity({ method: DidMethod.Iden3, blockchain: Blockchain.Polygon, @@ -366,7 +359,6 @@ describe('sig proofs', () => { ); }); - it('sigv2-ipfs-string-eq', async () => { const req = { id: '0d8e91e5-5686-49b5-85e3-2b35538c6a03', @@ -396,9 +388,6 @@ describe('sig proofs', () => { from: 'did:polygonid:polygon:mumbai:2qLPqvayNQz9TA2r5VPxUugoF18teGU583zJ859wfy' }; - const seedPhraseIssuer: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseedseed'); - const seedPhrase: Uint8Array = byteEncoder.encode('seedseedseedseedseedseedseeduser'); - const { did: userDID } = await idWallet.createIdentity({ method: DidMethod.Iden3, blockchain: Blockchain.Polygon, @@ -454,4 +443,95 @@ describe('sig proofs', () => { expect(vp).to.be.undefined; }); + + it('sigv2 vp-credential', async () => { + const query = { + allowedIssuers: ['*'], + context: 'ipfs://QmQXQ5gBNfJuc9QXy5pGbaVfLxzFjCDAvPs4Fa43BaU1U4', + credentialSubject: { + 'postalProviderInformation.name': {} + }, + type: 'DeliveryAddress' + }; + + const { did: issuerDID } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Polygon, + networkId: NetworkId.Mumbai, + seed: seedPhraseIssuer, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + + const { did: userDID } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Polygon, + networkId: NetworkId.Mumbai, + seed: seedPhrase, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + + const claimReq: CredentialRequest = { + credentialSchema: 'ipfs://QmbLQKw9Mzc9fVHowatJbvZjWNSUZchxYQX5Wtt8Ff9rGx', + type: 'DeliveryAddress', + credentialSubject: { + id: userDID.toString(), + price: 10, + deliveryTime: '2023-07-11T16:05:51.140Z', + postalProviderInformation: { + name: 'ukr posta', + officeNo: 1 + }, + homeAddress: { + line2: 'line 2', + line1: 'line 1' + }, + isPostalProvider: true + }, + expiration: 1693526400, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }; + const issuerCred = await idWallet.issueCredential(issuerDID, claimReq, { + ipfsGatewayURL: 'https://ipfs.io' + }); + + await credWallet.save(issuerCred); + + const creds = await credWallet.findByQuery(query); + expect(creds.length).to.not.equal(0); + + const credsForMyUserDID = await credWallet.filterByCredentialSubject(creds, userDID); + expect(credsForMyUserDID.length).to.equal(1); + const vpReq = { + id: 1, + circuitId: 'credentialAtomicQuerySigV2', + query + }; + const { proof, vp } = await proofService.generateProof(vpReq, userDID, credsForMyUserDID[0]); + expect(proof).not.to.be.undefined; + + expect(vp).to.deep.equal({ + '@context': ['https://www.w3.org/2018/credentials/v1'], + '@type': 'VerifiablePresentation', + verifiableCredential: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'ipfs://QmQXQ5gBNfJuc9QXy5pGbaVfLxzFjCDAvPs4Fa43BaU1U4' + ], + '@type': ['VerifiableCredential', 'DeliveryAddress'], + credentialSubject: { + '@type': 'DeliveryAddress', + postalProviderInformation: { name: 'ukr posta' } + } + } + }); + }); });