diff --git a/apps/shared/src/events/credentialEvents.ts b/apps/shared/src/events/credentialEvents.ts index b71dd2cd81a2b8a806fbe9b486d4c3c9988b970a..49a495d2b4ee44d0df77ac9343874162dd642b2f 100644 --- a/apps/shared/src/events/credentialEvents.ts +++ b/apps/shared/src/events/credentialEvents.ts @@ -54,6 +54,8 @@ export type EventDidcommAnonCredsCredentialsOfferInput = BaseEventInput<{ connectionId: string; credentialDefinitionId: string; attributes: Array<{ name: string; value: string; mimeType?: string }>; + revocationRegistryDefinitionId?: string; + revocationRegistryIndex?: number; }>; export class EventDidcommAnonCredsCredentialsOffer extends BaseEvent<CredentialExchangeRecord> { public static token = 'didcomm.anoncreds.credentials.offer'; diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts index d9792cb426f8e0d39524e4fefff9231f2e872e94..9ac30b03afffda1f588cdcf5890f819b76629a8e 100644 --- a/apps/ssi-abstraction/src/agent/agent.service.ts +++ b/apps/ssi-abstraction/src/agent/agent.service.ts @@ -13,10 +13,12 @@ import { import { AskarModule } from '@credo-ts/askar'; import { Agent, + CacheModule, ConnectionsModule, CredentialsModule, DidsModule, HttpOutboundTransport, + InMemoryLruCache, JwkDidRegistrar, JwkDidResolver, KeyDidRegistrar, @@ -131,6 +133,8 @@ export class AgentService implements OnApplicationShutdown { ], }), + cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 500 }) }), + anoncreds: new AnonCredsModule({ anoncreds, registries: [new IndyVdrAnonCredsRegistry()], diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts index c994a5a237402a5dc0cba4b42de2f823532c4358..a00ce2c0a2238f180ca1354bfe0c7ea802df3e73 100644 --- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts +++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts @@ -141,6 +141,8 @@ export class AnonCredsCredentialsService { connectionId, credentialDefinitionId, attributes, + revocationRegistryIndex, + revocationRegistryDefinitionId, }: EventDidcommAnonCredsCredentialsOfferInput): Promise< EventDidcommAnonCredsCredentialsOffer['data'] > { @@ -149,7 +151,12 @@ export class AnonCredsCredentialsService { protocolVersion: 'v2', connectionId, credentialFormats: { - anoncreds: { credentialDefinitionId, attributes }, + anoncreds: { + credentialDefinitionId, + attributes, + revocationRegistryDefinitionId, + revocationRegistryIndex, + }, }, }), ); @@ -159,6 +166,8 @@ export class AnonCredsCredentialsService { tenantId, credentialDefinitionId, attributes, + revocationRegistryIndex, + revocationRegistryDefinitionId, }: EventDidcommAnonCredsCredentialsOfferToSelfInput): Promise< EventDidcommAnonCredsCredentialsOfferToSelf['data'] > { @@ -186,7 +195,12 @@ export class AnonCredsCredentialsService { autoAcceptCredential: AutoAcceptCredential.Always, connectionId: connection.id, credentialFormats: { - anoncreds: { credentialDefinitionId, attributes }, + anoncreds: { + credentialDefinitionId, + attributes, + revocationRegistryDefinitionId, + revocationRegistryIndex, + }, }, }); }); diff --git a/apps/ssi-abstraction/src/agent/dids/dids.service.ts b/apps/ssi-abstraction/src/agent/dids/dids.service.ts index a1888d53f321a466877ff4b9788e53148ba5aa30..ed1fb32976f7dc6926d4d9b8872a3ae881e2c981 100644 --- a/apps/ssi-abstraction/src/agent/dids/dids.service.ts +++ b/apps/ssi-abstraction/src/agent/dids/dids.service.ts @@ -169,7 +169,7 @@ export class DidsService { }), ); - const buffer = Hasher.hash(publicKey, 'sha2-256'); + const buffer = Hasher.hash(publicKey, 'sha-256'); const id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)); const verkey = publicKeyBase58; diff --git a/apps/ssi-abstraction/test/revocation.e2e-spec.ts b/apps/ssi-abstraction/test/revocation.e2e-spec.ts index 83b176744e2de88e70183fdc406d3948120ba38c..1fdab05ea5fde1f8dd30a4e0ec6d687289e8abef 100644 --- a/apps/ssi-abstraction/test/revocation.e2e-spec.ts +++ b/apps/ssi-abstraction/test/revocation.e2e-spec.ts @@ -1,10 +1,22 @@ import type { INestApplication } from '@nestjs/common'; import type { ClientProxy } from '@nestjs/microservices'; -import type { EventAnonCredsRevocationRegisterRevocationRegistryDefinitionInput } from '@ocm/shared'; +import type { + EventAnonCredsCredentialsGetAllInput, + EventAnonCredsRevocationRegisterRevocationRegistryDefinitionInput, + EventAnonCredsRevocationRegisterRevocationStatusListInput, + EventAnonCredsRevocationRevokeInput, + EventDidcommAnonCredsCredentialsOfferToSelfInput, +} from '@ocm/shared'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { EventAnonCredsRevocationRegisterRevocationRegistryDefinition } from '@ocm/shared'; +import { + EventAnonCredsCredentialsGetAll, + EventDidcommAnonCredsCredentialsOfferToSelf, + EventAnonCredsRevocationRegisterRevocationRegistryDefinition, + EventAnonCredsRevocationRegisterRevocationStatusList, + EventAnonCredsRevocationRevoke, +} from '@ocm/shared'; import { randomBytes } from 'crypto'; import { firstValueFrom } from 'rxjs'; @@ -100,7 +112,7 @@ describe('Revocation', () => { client.close(); }); - it( + xit( EventAnonCredsRevocationRegisterRevocationRegistryDefinition.token, async () => { const response$ = client.send< @@ -121,9 +133,144 @@ describe('Revocation', () => { expect( eventInstance.instance.revocationRegistryDefinitionId.startsWith( - 'did:indy:bcorvin:test:', + 'did:indy:bcovrin:test:', ), ).toBeTruthy(); }, ); + + xit(EventAnonCredsRevocationRegisterRevocationStatusList.token, async () => { + let revRegDefId: string = ''; + { + const response$ = client.send< + EventAnonCredsRevocationRegisterRevocationRegistryDefinition, + EventAnonCredsRevocationRegisterRevocationRegistryDefinitionInput + >(EventAnonCredsRevocationRegisterRevocationRegistryDefinition.token, { + tenantId, + tag: 'rev-tag-two', + issuerDid, + credentialDefinitionId, + maximumCredentialNumber: 100, + }); + const response = await firstValueFrom(response$); + const eventInstance = + EventAnonCredsRevocationRegisterRevocationRegistryDefinition.fromEvent( + response, + ); + + revRegDefId = eventInstance.instance.revocationRegistryDefinitionId; + } + + const response$ = client.send< + EventAnonCredsRevocationRegisterRevocationStatusList, + EventAnonCredsRevocationRegisterRevocationStatusListInput + >(EventAnonCredsRevocationRegisterRevocationStatusList.token, { + tenantId, + revocationRegistryDefinitionId: revRegDefId, + issuerDid, + }); + const response = await firstValueFrom(response$); + const eventInstance = + EventAnonCredsRevocationRegisterRevocationStatusList.fromEvent(response); + + expect(eventInstance.instance).toMatchObject({}); + }); + + it(EventAnonCredsRevocationRevoke.token, async () => { + let revRegDefId: string = ''; + let credentialId: string = ''; + + // Register the revocation registry definition + { + const response$ = client.send< + EventAnonCredsRevocationRegisterRevocationRegistryDefinition, + EventAnonCredsRevocationRegisterRevocationRegistryDefinitionInput + >(EventAnonCredsRevocationRegisterRevocationRegistryDefinition.token, { + tenantId, + tag: 'rev-tag-three', + issuerDid, + credentialDefinitionId, + maximumCredentialNumber: 100, + }); + const response = await firstValueFrom(response$); + const eventInstance = + EventAnonCredsRevocationRegisterRevocationRegistryDefinition.fromEvent( + response, + ); + + revRegDefId = eventInstance.instance.revocationRegistryDefinitionId; + } + + // Register the revocation Status List + { + const response$ = client.send< + EventAnonCredsRevocationRegisterRevocationStatusList, + EventAnonCredsRevocationRegisterRevocationStatusListInput + >(EventAnonCredsRevocationRegisterRevocationStatusList.token, { + tenantId, + revocationRegistryDefinitionId: revRegDefId, + issuerDid, + }); + const response = await firstValueFrom(response$); + const eventInstance = + EventAnonCredsRevocationRegisterRevocationStatusList.fromEvent( + response, + ); + + expect(eventInstance.instance).toMatchObject({}); + } + + // Issue a credential + { + const response$ = client.send< + EventDidcommAnonCredsCredentialsOfferToSelf, + EventDidcommAnonCredsCredentialsOfferToSelfInput + >(EventDidcommAnonCredsCredentialsOfferToSelf.token, { + tenantId, + attributes: [ + { name: 'Name', value: 'Berend' }, + { name: 'Age', value: '30' }, + ], + revocationRegistryDefinitionId: revRegDefId, + revocationRegistryIndex: 10, + credentialDefinitionId, + }); + const response = await firstValueFrom(response$); + const eventInstance = + EventDidcommAnonCredsCredentialsOfferToSelf.fromEvent(response); + + credentialId = eventInstance.instance.id; + } + + // Revoke the credential + { + const response$ = client.send< + EventAnonCredsRevocationRevoke, + EventAnonCredsRevocationRevokeInput + >(EventAnonCredsRevocationRevoke.token, { + tenantId, + credentialId, + }); + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsRevocationRevoke.fromEvent(response); + expect(eventInstance.instance).toBeNull(); + } + + // Check the state + { + const response$ = client.send< + EventAnonCredsCredentialsGetAll, + EventAnonCredsCredentialsGetAllInput + >(EventAnonCredsCredentialsGetAll.token, { + tenantId, + }); + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsCredentialsGetAll.fromEvent(response); + + const credRecord = eventInstance.instance.find( + (r) => r.id === credentialId, + ); + // console.log(credRecord); + } + }); });