Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multisig test of credential revocation. #225

Merged
merged 4 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
);
}
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
Loading