From 1ad0582ab4ea469c143ea48b17085d4592ff0620 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 1 Jun 2024 22:31:39 -0300 Subject: [PATCH 1/4] fix: unqualified indy revRegDefId in migration Signed-off-by: Ariel Gentile --- .../0.4-0.5/anonCredsCredentialRecord.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts index 6c2791ca40..53073483a7 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/anonCredsCredentialRecord.ts @@ -16,10 +16,11 @@ import { fetchCredentialDefinition } from '../../utils/anonCredsObjects' import { getIndyNamespaceFromIndyDid, getQualifiedDidIndyDid, - getUnQualifiedDidIndyDid, + getUnqualifiedRevocationRegistryDefinitionId, isIndyDid, isUnqualifiedCredentialDefinitionId, isUnqualifiedIndyDid, + parseIndyRevocationRegistryId, } from '../../utils/indyIdentifiers' import { W3cAnonCredsCredentialMetadataKey } from '../../utils/metadata' import { getW3cRecordAnonCredsTags } from '../../utils/w3cAnonCredsUtils' @@ -156,12 +157,20 @@ async function migrateLegacyToW3cCredential(agentContext: AgentContext, legacyRe credentialRecordId: w3cCredentialRecord.id, } - // If using unqualified dids, store both qualified/unqualified revRegId forms + // If using Indy dids, store both qualified/unqualified revRegId forms // to allow retrieving it from revocation notification service if (legacyTags.revocationRegistryId && indyNamespace) { + const { credentialDefinitionTag, namespaceIdentifier, revocationRegistryTag, schemaSeqNo } = + parseIndyRevocationRegistryId(legacyTags.revocationRegistryId) + relatedCredentialExchangeRecord.setTags({ anonCredsRevocationRegistryId: getQualifiedDidIndyDid(legacyTags.revocationRegistryId, indyNamespace), - anonCredsUnqualifiedRevocationRegistryId: getUnQualifiedDidIndyDid(legacyTags.revocationRegistryId), + anonCredsUnqualifiedRevocationRegistryId: getUnqualifiedRevocationRegistryDefinitionId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ), }) } @@ -190,13 +199,14 @@ export async function storeAnonCredsInW3cFormatV0_5(age try { await migrateLegacyToW3cCredential(agent.context, record) await anoncredsRepository.delete(agent.context, record) + agent.config.logger.debug( + `Successfully migrated w3c credential record with id ${record.id} to storage version 0.5` + ) } catch (error) { agent.config.logger.error( `Failed to migrate w3c credential record with id ${record.id} to storage version 0.5`, error ) } - - agent.config.logger.debug(`Successfully migrated w3c credential record with id ${record.id} to storage version 0.5`) } } From c0a0286b0532fd256187e7ca02507a1eb9946ec3 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 4 Jun 2024 10:10:32 -0300 Subject: [PATCH 2/4] test: add revocable credentials Signed-off-by: Ariel Gentile --- packages/anoncreds/src/models/exchange.ts | 2 + .../w3cCredentialRecordMigration.test.ts | 103 ++++++++++++++++-- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index b6726b2979..075e950220 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -52,6 +52,8 @@ export interface AnonCredsCredential { values: Record signature: unknown signature_correctness_proof: unknown + rev_reg?: unknown + witness?: unknown } export interface AnonCredsProof { diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index 92f896a14f..a8d39e9ab3 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -149,6 +149,16 @@ describe('0.4-0.5 | AnonCredsRecord', () => { }) }) + it('revocable credential with did:indy (sovrin) identifier', async () => { + await testMigration(agent, { + issuerId: 'did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgg', + schemaIssuerId: 'did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgf', + schemaId: 'did:indy:sovrin:LjgpST2rjsoxYegQDRm7EL/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + indyNamespace: 'sovrin', + revocable: true, + }) + }) + it('credential with unqualified did:indy (bcovrin:test) identifiers', async () => { await testMigration(agent, { issuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH'), @@ -160,6 +170,18 @@ describe('0.4-0.5 | AnonCredsRecord', () => { }) }) + it('revocable credential with unqualified did:indy (bcovrin:test) identifiers', async () => { + await testMigration(agent, { + issuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH'), + schemaIssuerId: getUnQualifiedDidIndyDid('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG'), + schemaId: getUnQualifiedDidIndyDid( + 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjG/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' + ), + indyNamespace: 'bcovrin:test', + revocable: true, + }) + }) + it('credential with cached unqualified did:indy (bcovrin:test) identifiers', async () => { inMemoryLruCache.get.mockReturnValueOnce({ indyNamespace: 'bcovrin:test' }) @@ -196,15 +218,16 @@ async function testMigration( schemaId: string indyNamespace?: string shouldBeInCache?: 'indy' | 'sov' + revocable?: boolean } ) { - const { issuerId, schemaIssuerId, schemaId, indyNamespace } = options + const { issuerId, schemaIssuerId, schemaId, indyNamespace, revocable } = options - const registry = await agentContext.dependencyManager + const registry = agentContext.dependencyManager .resolve(AnonCredsRegistryService) .getRegistryForIdentifier(agentContext, issuerId) - const registerCredentialDefinitionReturn = await registry.registerCredentialDefinition(agentContext, { + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { credentialDefinition: { schemaId: indyNamespace ? getQualifiedDidIndyDid(schemaId, indyNamespace) : schemaId, type: 'CL', @@ -229,13 +252,49 @@ async function testMigration( options: {}, }) - if (!registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId) + if (!credentialDefinitionState.credentialDefinitionId) throw new CredoError('Registering Credential Definition Failed') + // We'll use unqualified form in case the inputs are unqualified as well + const credentialDefinitionId = + indyNamespace && isUnqualifiedIndyDid(issuerId) + ? getUnQualifiedDidIndyDid(credentialDefinitionState.credentialDefinitionId) + : credentialDefinitionState.credentialDefinitionId + let revocationRegistryDefinitionId: string | undefined + + if (revocable) { + const { revocationRegistryDefinitionState } = await registry.registerRevocationRegistryDefinition(agentContext, { + revocationRegistryDefinition: { + credDefId: credentialDefinitionState.credentialDefinitionId, + issuerId: indyNamespace ? getQualifiedDidIndyDid(issuerId, indyNamespace) : issuerId, + revocDefType: 'CL_ACCUM', + value: { + publicKeys: { + accumKey: { + z: 'ab81257c-be63-4051-9e21-c7d384412f64', + }, + }, + maxCredNum: 100, + tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', + tailsLocation: 'http://localhost:7200/tails', + }, + tag: 'TAG', + }, + options: {}, + }) + if (!revocationRegistryDefinitionState.revocationRegistryDefinitionId) + throw new CredoError('Registering Revocation Registry Definition Failed') + + revocationRegistryDefinitionId = + indyNamespace && isUnqualifiedIndyDid(issuerId) + ? getUnQualifiedDidIndyDid(revocationRegistryDefinitionState.revocationRegistryDefinitionId) + : revocationRegistryDefinitionState.revocationRegistryDefinitionId + } + const anonCredsRecord = new AnonCredsCredentialRecord({ credential: { schema_id: schemaId, - cred_def_id: registerCredentialDefinitionReturn.credentialDefinitionState.credentialDefinitionId, + cred_def_id: credentialDefinitionState.credentialDefinitionId, values: { name: { raw: 'John', @@ -256,10 +315,23 @@ async function testMigration( se: '22707379000451320101568757017184696744124237924783723059712360528872398590682272715197914336834321599243107036831239336605987281577690130807752876870302232265860540101807563741012022740942625464987934377354684266599492895835685698819662114798915664525092894122648542269399563759087759048742378622062870244156257780544523627249100818371255142174054148531811440128609220992508274170196108004985441276737673328642493312249112077836369109453214857237693701603680205115444482751700483514317558743227403858290707747986550689265796031162549838465391957776237071049436590886476581821857234951536091662216488995258175202055258', c: '86499530658088050169174214946559930902913340880816576251403968391737698128027', }, - rev_reg_id: undefined, + rev_reg: revocable + ? { + accum: + '2 1ECC5AB3496DF286013468F9DC94FA57D2E0CB65809130F49493884DA849D88A 2 20F3F79A24E29B3DF958FA5471B68CAF2FBBAF8E3D3A1F8F17BC5E410242A1BE 2 071C3E27F50B72EB048E530E0A07AC87B5578A63678803D009A9D40E5D3E41B8 2 0E9330E77B1A56DE5C70C8D9B02658CF571F4465EA489A7CEA12CFDA1A311AF5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + } + : undefined, + witness: revocable + ? { + omega: + '2 024D139F10D86B41FDFE98064B5794D0AFEE6183192A7CC2007803532F38CDB9 2 0AC11C34FDEDCA60FFD23E4FC37C9FAFB29737990D6B7E81190AA8C1BF654034 2 04CCBF871DA8BAB94769B08CBE777E83994F121F8BE1F64D3DE90EC6E2401EA9 2 1539F896A2C98798624E2AE12A0D2941EE898570BE3F0F40E59928008F95C969 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + } + : undefined, + + rev_reg_id: revocable ? revocationRegistryDefinitionId : undefined, }, credentialId: 'myCredentialId', - credentialRevocationId: undefined, + credentialRevocationId: revocable ? '1' : undefined, linkSecretId: 'linkSecretId', issuerId, schemaIssuerId, @@ -286,6 +358,13 @@ async function testMigration( credentialRecordType: 'anoncreds', }, ], + tags: { + credentialDefinitionId, + issuerId, + revocationRegistryId: revocationRegistryDefinitionId, + schemaId, + schemaIssuerId, + }, }) mockFunction(credentialExchangeRepo.findByQuery).mockResolvedValue([initialCredentialExchangeRecord]) @@ -334,6 +413,16 @@ async function testMigration( }) ) + if (revocable) { + // TODO + expect(credentialExchangeRepo.update).toHaveBeenCalledWith( + agent.context, + expect.objectContaining({ + credentials: [{ credentialRecordType: 'w3c', credentialRecordId: w3cCredentialRecord.id }], + }) + ) + } + if (unqualifiedDidIndyDid && options.shouldBeInCache) { expect(inMemoryLruCache.get).toHaveReturnedWith({ indyNamespace }) } else if (unqualifiedDidIndyDid && !options.shouldBeInCache) { From 24485eab0d8cf2ccf2504722cba8067b0acdbf99 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 5 Jun 2024 10:17:27 -0300 Subject: [PATCH 3/4] test: minor change Signed-off-by: Ariel Gentile --- .../0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts index a8d39e9ab3..1a8afa526d 100644 --- a/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts +++ b/packages/anoncreds/src/updates/0.4-0.5/__tests__/w3cCredentialRecordMigration.test.ts @@ -294,7 +294,7 @@ async function testMigration( const anonCredsRecord = new AnonCredsCredentialRecord({ credential: { schema_id: schemaId, - cred_def_id: credentialDefinitionState.credentialDefinitionId, + cred_def_id: credentialDefinitionId, values: { name: { raw: 'John', From eaf0dc4268655a42dddb115b7b9b695a4d10cc8f Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 5 Jun 2024 15:05:46 -0300 Subject: [PATCH 4/4] ci: trigger DCO Signed-off-by: Ariel Gentile