From 2a795b767f55cfa3eec45531d5b78c71ef7240cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Wed, 20 Nov 2024 13:24:13 +0300 Subject: [PATCH 1/7] feat: nested ctype val --- .../credentials/src/V1/KiltCredentialV1.ts | 110 +++++++++++++++++- 1 file changed, 105 insertions(+), 5 deletions(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index 557322099..58144c67e 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -7,6 +7,7 @@ import { hexToU8a } from '@polkadot/util' import { base58Encode } from '@polkadot/util-crypto' +import * as Kilt from "@kiltprotocol/sdk-js" import { JsonSchema, SDKErrors } from '@kiltprotocol/utils' import type { @@ -327,15 +328,76 @@ export function fromInput({ const cachingCTypeLoader = newCachingCTypeLoader() +// Helper function to check if CType is nested +function cTypeTypeFinder(cType: ICType): boolean { + function hasRef(obj: any): boolean { + if (typeof obj !== 'object' || obj === null) return false + + if ('$ref' in obj) return true + + return Object.values(obj).some(value => { + if (Array.isArray(value)) { + return value.some(item => hasRef(item)) + } + if (typeof value === 'object') { + return hasRef(value) + } + return false + }) + } + return hasRef(cType.properties) +} + +// Helper function to extract unique references from CType +function extractUniqueReferences(cType: ICType): Set { + const references = new Set() + + function processValue(value: any) { + if (typeof value !== 'object' || value === null) return + + if ('$ref' in value) { + const ref = value['$ref'] + // Extract KILT CType reference + if (ref.startsWith('kilt:ctype:')) { + // Get first part split by #/ + const baseRef = ref.split('#/')[0] + references.add(baseRef) + } + } + + // Check all values of the object recursively + Object.values(value).forEach(v => processValue(v)) + } + + processValue(cType.properties) + return references +} + + /** * Validates the claims in the VC's `credentialSubject` against a CType definition. + * Supports both nested and non-nested CType validation. + * + * For non-nested CTypes: + * - Validates claims directly against the CType schema + * + * For nested CTypes: + * - Automatically detects nested structure through $ref properties + * - Fetches referenced CTypes from the blockchain + * - Performs validation against the main CType and all referenced CTypes * * @param credential A {@link KiltCredentialV1} type verifiable credential. * @param credential.credentialSubject The credentialSubject to be validated. * @param credential.type The credential's types. * @param options Options map. - * @param options.cTypes One or more CType definitions to be used for validation. If `loadCTypes` is set to `false`, validation will fail if the definition of the credential's CType is not given. - * @param options.loadCTypes A function to load CType definitions that are not in `cTypes`. Defaults to using the {@link newCachingCTypeLoader | CachingCTypeLoader}. If set to `false` or `undefined`, no additional CTypes will be loaded. + * @param options.cTypes One or more CType definitions to be used for validation. If loadCTypes is set to false, validation will fail if the definition of the credential's CType is not given. + * @param options.loadCTypes A function to load CType definitions that are not in cTypes. Defaults to using the {@link newCachingCTypeLoader | CachingCTypeLoader}. If set to false or undefined, no additional CTypes will be loaded. + * + * @throws {Error} If the credential type does not contain a valid CType id + * @throws {Error} If required CType definitions cannot be loaded + * @throws {Error} If claims do not follow the expected CType format + * @throws {Error} If referenced CTypes in nested structure cannot be fetched from the blockchain + * @throws {Error} If validation fails against the CType schema */ export async function validateSubject( { @@ -344,7 +406,7 @@ export async function validateSubject( }: Pick, { cTypes = [], - loadCTypes = cachingCTypeLoader, + loadCTypes = newCachingCTypeLoader(), }: { cTypes?: ICType[]; loadCTypes?: false | CTypeLoader } = {} ): Promise { // get CType id referenced in credential @@ -354,6 +416,7 @@ export async function validateSubject( if (!credentialsCTypeId) { throw new Error('credential type does not contain a valid CType id') } + // check that we have access to the right schema let cType = cTypes?.find(({ $id }) => $id === credentialsCTypeId) if (!cType) { @@ -385,6 +448,43 @@ export async function validateSubject( [key.substring(vocab.length)]: value, } }, {}) - // validates against CType (also validates CType schema itself) - CType.verifyClaimAgainstSchema(claims, cType) + + // Connect to blockchain + const api = Kilt.ConfigService.get('api') + + // Bizim eklediğimiz doğrulama mantığı + const isNested = cTypeTypeFinder(cType) + + if (!isNested) { + await CType.verifyClaimAgainstNestedSchemas( + cType, + [], + claims + ) + } else { + const references = extractUniqueReferences(cType) + const referencedCTypes: ICType[] = [] + + for (const ref of references) { + try { + const referencedCType = await CType.fetchFromChain(ref as any) + if (referencedCType.cType) { + referencedCTypes.push(referencedCType.cType) + } + } catch (error) { + console.error(`Failed to fetch CType for reference ${ref}:`, error) + throw new Error(`Failed to fetch CType from chain: ${ref}`) + } + } + + if (referencedCTypes.length === references.size) { + await CType.verifyClaimAgainstNestedSchemas( + cType, + referencedCTypes, + claims + ) + } else { + throw new Error("Some referenced CTypes could not be fetched") + } + } } From 77b9c3d89f213c51b6b428ade650cc04c50efc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Mon, 2 Dec 2024 22:33:34 +0300 Subject: [PATCH 2/7] fix: review changes --- .../credentials/src/V1/KiltCredentialV1.ts | 133 +++++++++--------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index 58144c67e..a4d85eb3a 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -7,7 +7,6 @@ import { hexToU8a } from '@polkadot/util' import { base58Encode } from '@polkadot/util-crypto' -import * as Kilt from "@kiltprotocol/sdk-js" import { JsonSchema, SDKErrors } from '@kiltprotocol/utils' import type { @@ -328,47 +327,53 @@ export function fromInput({ const cachingCTypeLoader = newCachingCTypeLoader() -// Helper function to check if CType is nested -function cTypeTypeFinder(cType: ICType): boolean { - function hasRef(obj: any): boolean { - if (typeof obj !== 'object' || obj === null) return false - - if ('$ref' in obj) return true - - return Object.values(obj).some(value => { - if (Array.isArray(value)) { - return value.some(item => hasRef(item)) - } - if (typeof value === 'object') { - return hasRef(value) - } - return false - }) +// Check recursively if a value has references +const hasRef = (value: unknown): boolean => { + if (typeof value !== 'object' || value === null) { + return false + } + + if ('$ref' in (value as Record)) { + return true + } + + if (Array.isArray(value)) { + return value.some(item => hasRef(item)) } - return hasRef(cType.properties) + + return Object.values(value as Record).some(v => hasRef(v)) } -// Helper function to extract unique references from CType -function extractUniqueReferences(cType: ICType): Set { - const references = new Set() - - function processValue(value: any) { - if (typeof value !== 'object' || value === null) return - - if ('$ref' in value) { - const ref = value['$ref'] - // Extract KILT CType reference - if (ref.startsWith('kilt:ctype:')) { - // Get first part split by #/ - const baseRef = ref.split('#/')[0] - references.add(baseRef) - } +// Single function to both check for references and extract them +function extractUniqueReferences( + cType: ICType, + references: Set = new Set() +): Set { + if (typeof cType?.properties !== 'object' || cType.properties === null) { + return references + } + + const processValue = (value: unknown): void => { + if (typeof value !== 'object' || value === null) { + return + } + + const objValue = value as Record + + if ('$ref' in objValue) { + const ref = objValue['$ref'] as string + if (ref.startsWith('kilt:ctype:')) { + references.add(ref.split('#/')[0]) } - - // Check all values of the object recursively - Object.values(value).forEach(v => processValue(v)) + } + + if (Array.isArray(value)) { + value.forEach(processValue) + } else { + Object.values(objValue).forEach(processValue) + } } - + processValue(cType.properties) return references } @@ -406,7 +411,7 @@ export async function validateSubject( }: Pick, { cTypes = [], - loadCTypes = newCachingCTypeLoader(), + loadCTypes = cachingCTypeLoader, }: { cTypes?: ICType[]; loadCTypes?: false | CTypeLoader } = {} ): Promise { // get CType id referenced in credential @@ -448,43 +453,41 @@ export async function validateSubject( [key.substring(vocab.length)]: value, } }, {}) - - // Connect to blockchain - const api = Kilt.ConfigService.get('api') - - // Bizim eklediğimiz doğrulama mantığı - const isNested = cTypeTypeFinder(cType) - if (!isNested) { - await CType.verifyClaimAgainstNestedSchemas( - cType, - [], - claims - ) - } else { + try { + // Find references - if none exist, will return empty Set const references = extractUniqueReferences(cType) - const referencedCTypes: ICType[] = [] - for (const ref of references) { - try { - const referencedCType = await CType.fetchFromChain(ref as any) - if (referencedCType.cType) { - referencedCTypes.push(referencedCType.cType) + // Load referenced CTypes in parallel - if no references, will be empty array + const referencedCTypes = await Promise.all( + Array.from(references).map(async (ref) => { + try { + const referencedCType = await cachingCTypeLoader(ref as any) + return referencedCType + } catch (error) { + console.error(`Failed to fetch CType for reference ${ref}:`, error) + throw error } - } catch (error) { - console.error(`Failed to fetch CType for reference ${ref}:`, error) - throw new Error(`Failed to fetch CType from chain: ${ref}`) - } - } + }) + ) - if (referencedCTypes.length === references.size) { + // Filter out any undefined or null values + const validCTypes = referencedCTypes.filter((ctype): ctype is ICType => + ctype !== undefined && ctype !== null + ) + + // Verify if all CTypes were fetched successfully + if (validCTypes.length === references.size) { await CType.verifyClaimAgainstNestedSchemas( cType, - referencedCTypes, + validCTypes, claims ) } else { throw new Error("Some referenced CTypes could not be fetched") } + } catch (error) { + console.error("Validation error:", error) + throw error } -} +} \ No newline at end of file From 9e2596eb270bb5e24e0008e6e76e43935ce3e5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Mon, 2 Dec 2024 22:46:26 +0300 Subject: [PATCH 3/7] fix: linter added --- .../credentials/src/V1/KiltCredentialV1.ts | 61 ++++++++----------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index a4d85eb3a..fce59363b 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -338,15 +338,15 @@ const hasRef = (value: unknown): boolean => { } if (Array.isArray(value)) { - return value.some(item => hasRef(item)) + return value.some((item) => hasRef(item)) } - return Object.values(value as Record).some(v => hasRef(v)) + return Object.values(value as Record).some((v) => hasRef(v)) } // Single function to both check for references and extract them function extractUniqueReferences( - cType: ICType, + cType: ICType, references: Set = new Set() ): Set { if (typeof cType?.properties !== 'object' || cType.properties === null) { @@ -357,11 +357,9 @@ function extractUniqueReferences( if (typeof value !== 'object' || value === null) { return } - - const objValue = value as Record - + const objValue = value as Record if ('$ref' in objValue) { - const ref = objValue['$ref'] as string + const ref = objValue.$ref as string if (ref.startsWith('kilt:ctype:')) { references.add(ref.split('#/')[0]) } @@ -378,18 +376,15 @@ function extractUniqueReferences( return references } - /** * Validates the claims in the VC's `credentialSubject` against a CType definition. * Supports both nested and non-nested CType validation. - * * For non-nested CTypes: - * - Validates claims directly against the CType schema - * + * - Validates claims directly against the CType schema. * For nested CTypes: - * - Automatically detects nested structure through $ref properties - * - Fetches referenced CTypes from the blockchain - * - Performs validation against the main CType and all referenced CTypes + * - Automatically detects nested structure through $ref properties. + * - Fetches referenced CTypes from the blockchain. + * - Performs validation against the main CType and all referenced CTypes. * * @param credential A {@link KiltCredentialV1} type verifiable credential. * @param credential.credentialSubject The credentialSubject to be validated. @@ -397,12 +392,12 @@ function extractUniqueReferences( * @param options Options map. * @param options.cTypes One or more CType definitions to be used for validation. If loadCTypes is set to false, validation will fail if the definition of the credential's CType is not given. * @param options.loadCTypes A function to load CType definitions that are not in cTypes. Defaults to using the {@link newCachingCTypeLoader | CachingCTypeLoader}. If set to false or undefined, no additional CTypes will be loaded. - * - * @throws {Error} If the credential type does not contain a valid CType id - * @throws {Error} If required CType definitions cannot be loaded - * @throws {Error} If claims do not follow the expected CType format - * @throws {Error} If referenced CTypes in nested structure cannot be fetched from the blockchain - * @throws {Error} If validation fails against the CType schema + * + * @throws {Error} If the credential type does not contain a valid CType id. + * @throws {Error} If required CType definitions cannot be loaded. + * @throws {Error} If claims do not follow the expected CType format. + * @throws {Error} If referenced CTypes in nested structure cannot be fetched from the blockchain. + * @throws {Error} If validation fails against the CType schema. */ export async function validateSubject( { @@ -453,41 +448,35 @@ export async function validateSubject( [key.substring(vocab.length)]: value, } }, {}) - + try { - // Find references - if none exist, will return empty Set const references = extractUniqueReferences(cType) - - // Load referenced CTypes in parallel - if no references, will be empty array + const referencedCTypes = await Promise.all( Array.from(references).map(async (ref) => { try { const referencedCType = await cachingCTypeLoader(ref as any) return referencedCType } catch (error) { + // eslint-disable-next-line no-console console.error(`Failed to fetch CType for reference ${ref}:`, error) throw error } }) ) - // Filter out any undefined or null values - const validCTypes = referencedCTypes.filter((ctype): ctype is ICType => - ctype !== undefined && ctype !== null + const validCTypes = referencedCTypes.filter( + (ctype): ctype is ICType => ctype !== undefined && ctype !== null ) - // Verify if all CTypes were fetched successfully if (validCTypes.length === references.size) { - await CType.verifyClaimAgainstNestedSchemas( - cType, - validCTypes, - claims - ) + await CType.verifyClaimAgainstNestedSchemas(cType, validCTypes, claims) } else { - throw new Error("Some referenced CTypes could not be fetched") + throw new Error('Some referenced CTypes could not be fetched') } } catch (error) { - console.error("Validation error:", error) + // eslint-disable-next-line no-console + console.error('Validation error:', error) throw error } -} \ No newline at end of file +} From d8513816708fb33baefbd6750b100779ad886b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Mon, 2 Dec 2024 22:51:00 +0300 Subject: [PATCH 4/7] fix linter2 --- packages/credentials/src/V1/KiltCredentialV1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index fce59363b..81a38a632 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -357,7 +357,7 @@ function extractUniqueReferences( if (typeof value !== 'object' || value === null) { return } - const objValue = value as Record + const objValue = value as Record if ('$ref' in objValue) { const ref = objValue.$ref as string if (ref.startsWith('kilt:ctype:')) { From bbfced24e1897748e57f40ddc5fa06267ed4962c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Tue, 3 Dec 2024 17:00:24 +0300 Subject: [PATCH 5/7] fix: review2 --- .../credentials/src/V1/KiltCredentialV1.ts | 101 +++++++----------- 1 file changed, 37 insertions(+), 64 deletions(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index 81a38a632..a927ee100 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -327,23 +327,6 @@ export function fromInput({ const cachingCTypeLoader = newCachingCTypeLoader() -// Check recursively if a value has references -const hasRef = (value: unknown): boolean => { - if (typeof value !== 'object' || value === null) { - return false - } - - if ('$ref' in (value as Record)) { - return true - } - - if (Array.isArray(value)) { - return value.some((item) => hasRef(item)) - } - - return Object.values(value as Record).some((v) => hasRef(v)) -} - // Single function to both check for references and extract them function extractUniqueReferences( cType: ICType, @@ -353,26 +336,27 @@ function extractUniqueReferences( return references } - const processValue = (value: unknown): void => { - if (typeof value !== 'object' || value === null) { - return - } - const objValue = value as Record - if ('$ref' in objValue) { - const ref = objValue.$ref as string - if (ref.startsWith('kilt:ctype:')) { - references.add(ref.split('#/')[0]) - } - } + const objValue = cType.properties as Record - if (Array.isArray(value)) { - value.forEach(processValue) - } else { - Object.values(objValue).forEach(processValue) + if ('$ref' in objValue) { + const ref = objValue.$ref as string + if (ref.startsWith('kilt:ctype:')) { + references.add(ref.split('#/')[0]) } } - processValue(cType.properties) + if (Array.isArray(objValue)) { + objValue.forEach((item) => + extractUniqueReferences(item as ICType, references) + ) + } else { + Object.values(objValue).forEach((value) => { + if (typeof value === 'object' && value !== null) { + extractUniqueReferences(value as ICType, references) + } + }) + } + return references } @@ -409,7 +393,6 @@ export async function validateSubject( loadCTypes = cachingCTypeLoader, }: { cTypes?: ICType[]; loadCTypes?: false | CTypeLoader } = {} ): Promise { - // get CType id referenced in credential const credentialsCTypeId = type.find((str) => str.startsWith('kilt:ctype:') ) as ICType['$id'] @@ -417,7 +400,6 @@ export async function validateSubject( throw new Error('credential type does not contain a valid CType id') } - // check that we have access to the right schema let cType = cTypes?.find(({ $id }) => $id === credentialsCTypeId) if (!cType) { if (typeof loadCTypes !== 'function') { @@ -431,7 +413,6 @@ export async function validateSubject( } } - // normalize credential subject to form expected by CType schema const expandedClaims: Record = jsonLdExpandCredentialSubject(credentialSubject) delete expandedClaims['@id'] @@ -449,34 +430,26 @@ export async function validateSubject( } }, {}) - try { - const references = extractUniqueReferences(cType) - - const referencedCTypes = await Promise.all( - Array.from(references).map(async (ref) => { - try { - const referencedCType = await cachingCTypeLoader(ref as any) - return referencedCType - } catch (error) { - // eslint-disable-next-line no-console - console.error(`Failed to fetch CType for reference ${ref}:`, error) - throw error - } - }) - ) + const references = extractUniqueReferences(cType) - const validCTypes = referencedCTypes.filter( - (ctype): ctype is ICType => ctype !== undefined && ctype !== null - ) - - if (validCTypes.length === references.size) { - await CType.verifyClaimAgainstNestedSchemas(cType, validCTypes, claims) - } else { - throw new Error('Some referenced CTypes could not be fetched') - } - } catch (error) { - // eslint-disable-next-line no-console - console.error('Validation error:', error) - throw error + const referencedCTypes = await Promise.all( + Array.from(references).map(async (ref) => { + if (typeof loadCTypes !== 'function') { + throw new Error( + `The definition for this credential's CType ${ref} has not been passed to the validator and CType loading has been disabled` + ) + } + return loadCTypes(ref as any) + }) + ) + + const validCTypes = referencedCTypes.filter( + (ctype): ctype is ICType => ctype !== undefined && ctype !== null + ) + + if (validCTypes.length === references.size) { + await CType.verifyClaimAgainstNestedSchemas(cType, validCTypes, claims) + } else { + throw new Error('Some referenced CTypes could not be fetched') } } From a97c06645bfa3f8a18f7e47010b0d5a2e570a55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Mon, 16 Dec 2024 22:46:44 +0300 Subject: [PATCH 6/7] fix: infinite nest --- .../credentials/src/V1/KiltCredentialV1.ts | 88 +++++++++++-------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index a927ee100..9fb2e2003 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -327,37 +327,59 @@ export function fromInput({ const cachingCTypeLoader = newCachingCTypeLoader() -// Single function to both check for references and extract them -function extractUniqueReferences( +async function loadNestedCTypeDefinitions( cType: ICType, - references: Set = new Set() -): Set { - if (typeof cType?.properties !== 'object' || cType.properties === null) { - return references - } + cTypeLoader: (id: string) => Promise +): Promise> { + const fetchedCTypeIds = new Set() + const fetchedCTypeDefinitions = new Set() - const objValue = cType.properties as Record + async function processValue(value: unknown): Promise { + if (typeof value !== 'object' || value === null) { + return + } - if ('$ref' in objValue) { - const ref = objValue.$ref as string - if (ref.startsWith('kilt:ctype:')) { - references.add(ref.split('#/')[0]) + if (Array.isArray(value)) { + await Promise.all(value.map(processValue)) + return } - } - if (Array.isArray(objValue)) { - objValue.forEach((item) => - extractUniqueReferences(item as ICType, references) - ) - } else { - Object.values(objValue).forEach((value) => { - if (typeof value === 'object' && value !== null) { - extractUniqueReferences(value as ICType, references) + // Check if value is an object with $ref + const objValue = value as Record + if ('$ref' in objValue) { + const ref = objValue.$ref + if (typeof ref === 'string' && ref.startsWith('kilt:ctype:')) { + const cTypeId = ref.split('#/')[0] + + if (!fetchedCTypeIds.has(cTypeId)) { + fetchedCTypeIds.add(cTypeId) + const referencedCType = await cTypeLoader(cTypeId) + + if (referencedCType === undefined || referencedCType === null) { + throw new Error(`Failed to load referenced CType: ${cTypeId}`) + } + + fetchedCTypeDefinitions.add(referencedCType) + + const { properties } = referencedCType + if (properties !== undefined && properties !== null) { + await Promise.all(Object.values(properties).map(processValue)) + } + } } - }) + return + } + + // Process all values in the object + await Promise.all(Object.values(objValue).map(processValue)) } - return references + const { properties } = cType + if (properties !== undefined && properties !== null) { + await Promise.all(Object.values(properties).map(processValue)) + } + + return fetchedCTypeDefinitions } /** @@ -430,24 +452,16 @@ export async function validateSubject( } }, {}) - const references = extractUniqueReferences(cType) - - const referencedCTypes = await Promise.all( - Array.from(references).map(async (ref) => { - if (typeof loadCTypes !== 'function') { - throw new Error( - `The definition for this credential's CType ${ref} has not been passed to the validator and CType loading has been disabled` - ) - } - return loadCTypes(ref as any) - }) - ) + // Load all nested CTypes + const referencedCTypes = await loadNestedCTypeDefinitions(cType, loadCTypes) - const validCTypes = referencedCTypes.filter( + // Convert Set to Array and filter out any undefined or null values + const validCTypes = Array.from(referencedCTypes).filter( (ctype): ctype is ICType => ctype !== undefined && ctype !== null ) - if (validCTypes.length === references.size) { + // Verify if all CTypes were fetched successfully + if (validCTypes.length === referencedCTypes.size) { await CType.verifyClaimAgainstNestedSchemas(cType, validCTypes, claims) } else { throw new Error('Some referenced CTypes could not be fetched') From 1adad9da8f6a89a7e21098b4ace1315926cd59c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aybars=20G=C3=B6ktu=C4=9F=20Ayan?= Date: Thu, 19 Dec 2024 13:04:58 +0300 Subject: [PATCH 7/7] fix: effective loader --- .../credentials/src/V1/KiltCredentialV1.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/credentials/src/V1/KiltCredentialV1.ts b/packages/credentials/src/V1/KiltCredentialV1.ts index 9fb2e2003..4cc2d5c09 100644 --- a/packages/credentials/src/V1/KiltCredentialV1.ts +++ b/packages/credentials/src/V1/KiltCredentialV1.ts @@ -452,8 +452,31 @@ export async function validateSubject( } }, {}) + // Create a type-safe loader function + const effectiveLoader = async (id: string): Promise => { + // Ensure the ID is in the correct format + if (!id.startsWith('kilt:ctype:0x')) { + throw new Error(`Invalid CType ID format: ${id}`) + } + + const typedId = id as `kilt:ctype:0x${string}` + + if (typeof loadCTypes === 'function') { + return loadCTypes(typedId) + } + const found = cTypes.find((ct) => ct.$id === typedId) + if (found) { + return found + } + throw new Error( + `CType ${id} not found in provided cTypes array and CType loading is disabled` + ) + } // Load all nested CTypes - const referencedCTypes = await loadNestedCTypeDefinitions(cType, loadCTypes) + const referencedCTypes = await loadNestedCTypeDefinitions( + cType, + effectiveLoader + ) // Convert Set to Array and filter out any undefined or null values const validCTypes = Array.from(referencedCTypes).filter(