From d6dac2c1a1e27b1f38becb87250eaa364389ce2c Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 15:42:51 -0800 Subject: [PATCH 1/4] Multisig test of credential revocation. Several fixes to revocation to make it work. --- examples/integration-scripts/multisig.test.ts | 100 +++++++++++++++++- src/keri/app/credentialing.ts | 25 ++++- 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/examples/integration-scripts/multisig.test.ts b/examples/integration-scripts/multisig.test.ts index 7eb75625..0f0b8604 100644 --- a/examples/integration-scripts/multisig.test.ts +++ b/examples/integration-scripts/multisig.test.ts @@ -904,6 +904,8 @@ test('multisig', async function run() { res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; + const credentialSaid = exn.e.acdc.d + const credRes2 = await client2.credentials().issue({ issuerName: 'multisig', registryId: regk2, @@ -1115,7 +1117,64 @@ test('multisig', async function run() { await assertOperations(client1, client2, client3, client4); await warnNotifications(client1, client2, client3, client4); -}, 360000); + + console.log('Revoking credential...') + const REVTIME = new Date().toISOString().replace('Z', '000+00:00'); + const revokeRes = await client1.credentials().revoke( + 'multisig', + credentialSaid, + REVTIME + ); + op1 = revokeRes.op; + + await multisigRevoke(client1, 'member1', 'multisig', revokeRes.rev, revokeRes.anc); + + console.log( + 'Member1 initiated credential revocation, waiting for others to join...' + ); + + // Member2 check for notifications and join the credential create event + msgSaid = await waitAndMarkNotification(client2, '/multisig/rev'); + console.log( + 'Member2 received exchange message to join the credential revocation event' + ); + res = await client2.groups().getRequest(msgSaid); + + const revokeRes2 = await client2.credentials().revoke( + 'multisig', + credentialSaid, + REVTIME + ); + + op2 = revokeRes2.op; + await multisigRevoke(client2, 'member2', 'multisig', revokeRes2.rev, revokeRes2.anc); + console.log('Member2 joins credential revoke event, waiting for others...'); + + // Member3 check for notifications and join the create registry event + msgSaid = await waitAndMarkNotification(client3, '/multisig/rev'); + console.log( + 'Member3 received exchange message to join the credential revocation event' + ); + res = await client3.groups().getRequest(msgSaid); + + const revokeRes3 = await client3.credentials().revoke( + 'multisig', + credentialSaid, + REVTIME + ); + + op3 = revokeRes3.op; + + await multisigRevoke(client3, 'member3', 'multisig', revokeRes3.rev, revokeRes3.anc); + console.log('Member3 joins credential revoke event, waiting for others...'); + + // Check completion + op1 = await waitOperation(client1, op1); + op2 = await waitOperation(client2, op2); + op3 = await waitOperation(client3, op3); + console.log('Multisig credential revocation completed!'); + +}, 400000); async function waitAndMarkNotification(client: SignifyClient, route: string) { const notes = await waitForNotifications(client, route); @@ -1175,3 +1234,42 @@ async function multisigIssue( recipients ); } + +async function multisigRevoke( + client: SignifyClient, + memberName: string, + groupName: string, + rev: Serder, + anc: Serder +) { + const leaderHab = await client.identifiers().get(memberName); + const groupHab = await client.identifiers().get(groupName); + const members = await client.identifiers().members(groupName); + + const keeper = client.manager!.get(groupHab); + const sigs = await keeper.sign(signify.b(anc.raw)); + const sigers = sigs.map((sig: string) => new signify.Siger({ qb64: sig })); + const ims = signify.d(signify.messagize(anc, sigers)); + const atc = ims.substring(anc.size); + + const embeds = { + iss: [rev, ''], + anc: [anc, atc], + }; + + const recipients = members.signing + .map((m: { aid: string }) => m.aid) + .filter((aid: string) => aid !== leaderHab.prefix); + + await client + .exchanges() + .send( + memberName, + 'multisig', + leaderHab, + '/multisig/rev', + { gid: groupHab.prefix }, + embeds, + recipients + ); +} diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 058ccdd4..5672a0a5 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -90,6 +90,12 @@ export interface IssueCredentialResult { op: Operation; } +export interface RevokeCredentialResult { + anc: Serder; + rev: Serder; + op: Operation; +} + export interface IpexGrantArgs { /** * Alias for the IPEX sender AID @@ -272,14 +278,16 @@ export class Credentials { * @async * @param {string} name Name or alias of the identifier * @param {string} said SAID of the credential + * @param {string} datetime date time of revocation * @returns {Promise} A promise to the long-running operation */ - async revoke(name: string, said: string): Promise { + async revoke(name: string, said: string, datetime?:string): Promise { const hab = await this.client.identifiers().get(name); const pre: string = hab.prefix; const vs = versify(Ident.KERI, undefined, Serials.JSON, 0); - const dt = new Date().toISOString().replace('Z', '000+00:00'); + const dt = + datetime ?? new Date().toISOString().replace('Z', '000+00:00'); const cred = await this.get(said); @@ -318,6 +326,9 @@ export class Credentials { d: rev.d, }, ]; + + const keeper = this.client!.manager!.get(hab); + if (estOnly) { // TODO implement rotation event throw new Error('Establishment only not implemented'); @@ -330,7 +341,6 @@ export class Credentials { version: undefined, kind: undefined, }); - const keeper = this.client!.manager!.get(hab); sigs = await keeper.sign(b(serder.raw)); ixn = serder.ked; } @@ -339,6 +349,7 @@ export class Credentials { rev: rev, ixn: ixn, sigs: sigs, + [keeper.algo]: keeper.params(), }; const path = `/identifiers/${name}/credentials/${said}`; @@ -347,7 +358,13 @@ export class Credentials { Accept: 'application/json+cesr', }); const res = await this.client.fetch(path, method, body, headers); - return await res.json(); + const op = await res.json(); + + return { + rev: new Serder(rev), + anc: new Serder(ixn), + op, + }; } /** From 5795091dc74efb32886274d34b6c5430cd3abef3 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 15:49:41 -0800 Subject: [PATCH 2/4] prettier --- examples/integration-scripts/multisig.test.ts | 53 +++++++++++-------- src/keri/app/credentialing.ts | 6 ++- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/examples/integration-scripts/multisig.test.ts b/examples/integration-scripts/multisig.test.ts index 0f0b8604..ae03baaf 100644 --- a/examples/integration-scripts/multisig.test.ts +++ b/examples/integration-scripts/multisig.test.ts @@ -904,7 +904,7 @@ test('multisig', async function run() { res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; - const credentialSaid = exn.e.acdc.d + const credentialSaid = exn.e.acdc.d; const credRes2 = await client2.credentials().issue({ issuerName: 'multisig', @@ -1118,16 +1118,20 @@ test('multisig', async function run() { await assertOperations(client1, client2, client3, client4); await warnNotifications(client1, client2, client3, client4); - console.log('Revoking credential...') + console.log('Revoking credential...'); const REVTIME = new Date().toISOString().replace('Z', '000+00:00'); - const revokeRes = await client1.credentials().revoke( - 'multisig', - credentialSaid, - REVTIME - ); + const revokeRes = await client1 + .credentials() + .revoke('multisig', credentialSaid, REVTIME); op1 = revokeRes.op; - await multisigRevoke(client1, 'member1', 'multisig', revokeRes.rev, revokeRes.anc); + await multisigRevoke( + client1, + 'member1', + 'multisig', + revokeRes.rev, + revokeRes.anc + ); console.log( 'Member1 initiated credential revocation, waiting for others to join...' @@ -1140,14 +1144,18 @@ test('multisig', async function run() { ); res = await client2.groups().getRequest(msgSaid); - const revokeRes2 = await client2.credentials().revoke( - 'multisig', - credentialSaid, - REVTIME - ); + const revokeRes2 = await client2 + .credentials() + .revoke('multisig', credentialSaid, REVTIME); op2 = revokeRes2.op; - await multisigRevoke(client2, 'member2', 'multisig', revokeRes2.rev, revokeRes2.anc); + await multisigRevoke( + client2, + 'member2', + 'multisig', + revokeRes2.rev, + revokeRes2.anc + ); console.log('Member2 joins credential revoke event, waiting for others...'); // Member3 check for notifications and join the create registry event @@ -1157,15 +1165,19 @@ test('multisig', async function run() { ); res = await client3.groups().getRequest(msgSaid); - const revokeRes3 = await client3.credentials().revoke( - 'multisig', - credentialSaid, - REVTIME - ); + const revokeRes3 = await client3 + .credentials() + .revoke('multisig', credentialSaid, REVTIME); op3 = revokeRes3.op; - await multisigRevoke(client3, 'member3', 'multisig', revokeRes3.rev, revokeRes3.anc); + await multisigRevoke( + client3, + 'member3', + 'multisig', + revokeRes3.rev, + revokeRes3.anc + ); console.log('Member3 joins credential revoke event, waiting for others...'); // Check completion @@ -1173,7 +1185,6 @@ test('multisig', async function run() { op2 = await waitOperation(client2, op2); op3 = await waitOperation(client3, op3); console.log('Multisig credential revocation completed!'); - }, 400000); async function waitAndMarkNotification(client: SignifyClient, route: string) { diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 5672a0a5..ebe3dfc5 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -281,7 +281,11 @@ export class Credentials { * @param {string} datetime date time of revocation * @returns {Promise} A promise to the long-running operation */ - async revoke(name: string, said: string, datetime?:string): Promise { + async revoke( + name: string, + said: string, + datetime?: string + ): Promise { const hab = await this.client.identifiers().get(name); const pre: string = hab.prefix; From b7ded242a8808383ee44260b4776c703a2d79126 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 19:39:26 -0800 Subject: [PATCH 3/4] Fix credentials test broken from revoke return. --- examples/integration-scripts/credentials.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/integration-scripts/credentials.test.ts b/examples/integration-scripts/credentials.test.ts index 220fa276..1babbc26 100644 --- a/examples/integration-scripts/credentials.test.ts +++ b/examples/integration-scripts/credentials.test.ts @@ -482,7 +482,7 @@ test('single signature credentials', async () => { .credentials() .revoke(issuerAid.name, qviCredentialId); - await waitOperation(issuerClient, revokeOperation); + await waitOperation(issuerClient, revokeOperation.op); const issuerCredential = await issuerClient .credentials() .get(qviCredentialId); From 6d2b51ca16cfb73af9f1d2cc0c54db82f8da59c4 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 19:43:02 -0800 Subject: [PATCH 4/4] Rev version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f43dea07..b8e95a00 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.2.0", + "version": "0.2.1", "license": "Apache-2.0", "exports": { ".": {