Skip to content

Commit

Permalink
Merge pull request #225 from pfeairheller/feat-multisig-rev
Browse files Browse the repository at this point in the history
Multisig test of credential revocation.
  • Loading branch information
pfeairheller authored Feb 26, 2024
2 parents 84657cd + 6d2b51c commit 3ec43a2
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 7 deletions.
2 changes: 1 addition & 1 deletion examples/integration-scripts/credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
111 changes: 110 additions & 1 deletion examples/integration-scripts/multisig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1115,7 +1117,75 @@ 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);
Expand Down Expand Up @@ -1175,3 +1245,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
);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.2.0",
"version": "0.2.1",
"license": "Apache-2.0",
"exports": {
".": {
Expand Down
29 changes: 25 additions & 4 deletions src/keri/app/credentialing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -272,14 +278,20 @@ 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<any>} A promise to the long-running operation
*/
async revoke(name: string, said: string): Promise<any> {
async revoke(
name: string,
said: string,
datetime?: string
): Promise<RevokeCredentialResult> {
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);

Expand Down Expand Up @@ -318,6 +330,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');
Expand All @@ -330,7 +345,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;
}
Expand All @@ -339,6 +353,7 @@ export class Credentials {
rev: rev,
ixn: ixn,
sigs: sigs,
[keeper.algo]: keeper.params(),
};

const path = `/identifiers/${name}/credentials/${said}`;
Expand All @@ -347,7 +362,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,
};
}

/**
Expand Down

0 comments on commit 3ec43a2

Please sign in to comment.