diff --git a/examples/integration-scripts/multisig-join.test.ts b/examples/integration-scripts/multisig-join.test.ts new file mode 100644 index 00000000..df782a6f --- /dev/null +++ b/examples/integration-scripts/multisig-join.test.ts @@ -0,0 +1,278 @@ +import signify, { Serder, SignifyClient } from 'signify-ts'; +import { resolveEnvironment } from './utils/resolve-env'; +import { getOrCreateClient, getOrCreateIdentifier } from './utils/test-setup'; +import { + markNotification, + waitForNotifications, + waitOperation, +} from './utils/test-util'; +import assert from 'assert'; + +const { vleiServerUrl, url } = resolveEnvironment(); + +describe('multisig-join', () => { + const nameMember1 = 'member1'; + const nameMember2 = 'member2'; + const nameMultisig = 'multisigGroup'; + + let client1: SignifyClient; + let client2: SignifyClient; + + beforeAll(async () => { + await signify.ready(); + + [client1, client2] = await Promise.all([ + getOrCreateClient(), + getOrCreateClient(), + ]); + + await Promise.all([ + createAID(client1, nameMember1, []), + createAID(client2, nameMember2, []), + ]); + + const [oobi1, oobi2] = await Promise.all([ + client1.oobis().get(nameMember1, 'agent'), + client2.oobis().get(nameMember2, 'agent'), + ]); + + const opOobi1 = await client1 + .oobis() + .resolve(oobi2.oobis[0], nameMember2); + const opOobi2 = await client2 + .oobis() + .resolve(oobi1.oobis[0], nameMember1); + await Promise.all([ + waitOperation(client1, opOobi1), + waitOperation(client2, opOobi2), + ]); + }); + + test('should create multisig', async () => { + const [aid1, aid2] = await Promise.all([ + client1.identifiers().get(nameMember1), + client2.identifiers().get(nameMember2), + ]); + const states = [aid1.state, aid2.state]; + const icpResult = await client1.identifiers().create(nameMultisig, { + algo: signify.Algos.group, + mhab: aid1, + isith: 1, + nsith: 1, + toad: aid1.state.b.length, + wits: aid1.state.b, + states: states, + rstates: states, + }); + + const createMultisig1 = await icpResult.op(); + + const serder = icpResult.serder; + + const sigs = icpResult.sigs; + const sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + + const ims = signify.d(signify.messagize(serder, sigers)); + const atc = ims.substring(serder.size); + const embeds = { + icp: [serder, atc], + }; + + const smids = [aid2.state.i]; + const recipients = [aid2.state.i]; + + await client1 + .exchanges() + .send( + nameMember1, + nameMultisig, + aid1, + '/multisig/icp', + { gid: serder.pre, smids: smids, rmids: smids }, + embeds, + recipients + ); + + const msgSaid = await waitAndMarkNotification(client2, '/multisig/icp'); + + const response = await client2.groups().getRequest(msgSaid); + const icp = response[0].exn.e.icp; + + const icpResult2 = await client2.identifiers().create(nameMultisig, { + algo: signify.Algos.group, + mhab: aid2, + isith: icp.kt, + nsith: icp.nt, + toad: parseInt(icp.bt), + wits: icp.b, + states, + rstates: states, + }); + const createMultisig2 = await icpResult2.op(); + + const [createResult1, createResult2] = await Promise.all([ + waitOperation(client1, createMultisig1), + waitOperation(client2, createMultisig2), + ]); + + assert.equal(createResult1.response.k[0], aid1.state.k[0]); + assert.equal(createResult1.response.k[1], aid2.state.k[0]); + assert.equal(createResult2.response.k[0], aid1.state.k[0]); + assert.equal(createResult2.response.k[1], aid2.state.k[0]); + + const members = await client1.identifiers().members(nameMultisig); + const signing = members.signing; + const eid1 = Object.keys(signing[0].ends.agent)[0]; + + const endRoleOperation = await client1 + .identifiers() + .addEndRole(nameMultisig, 'agent', eid1); + + await waitOperation(client1, await endRoleOperation.op()); + }); + + test('should add member3 to multisig', async () => { + const nameMember3 = 'member3'; + const client3 = await getOrCreateClient(); + + const aid3 = await createAID(client3, nameMember3, []); + + const [oobi1, oobi2, oobi3, oobi4] = await Promise.all([ + client1.oobis().get(nameMember1, 'agent'), + client2.oobis().get(nameMember2, 'agent'), + client3.oobis().get(nameMember3, 'agent'), + client1.oobis().get(nameMultisig, 'agent'), + ]); + + const oobiMultisig = oobi4.oobis[0].split('/agent/')[0]; + + const [opOobi1, opOobi2, opOobi3, opOobi4, opOobi5] = await Promise.all( + [ + client1.oobis().resolve(oobi3.oobis[0], nameMember3), + client2.oobis().resolve(oobi3.oobis[0], nameMember3), + client3.oobis().resolve(oobi1.oobis[0], nameMember1), + client3.oobis().resolve(oobi2.oobis[0], nameMember2), + client3.oobis().resolve(oobiMultisig, nameMultisig), + ] + ); + await Promise.all([ + waitOperation(client1, opOobi1), + waitOperation(client2, opOobi2), + waitOperation(client3, opOobi3), + waitOperation(client3, opOobi4), + waitOperation(client3, opOobi5), + ]); + + const [rotateResult1, rotateResult2] = await Promise.all([ + client1.identifiers().rotate(nameMember1), + client2.identifiers().rotate(nameMember2), + ]); + + await Promise.all([ + waitOperation(client1, await rotateResult1.op()), + waitOperation(client2, await rotateResult2.op()), + ]); + + const [aid1, aid2] = await Promise.all([ + client1.identifiers().get(nameMember1), + client2.identifiers().get(nameMember2), + ]); + + const updates = await Promise.all([ + await client1.keyStates().query(aid2.prefix, '1'), + await client1.keyStates().query(aid3.prefix, '0'), + await client2.keyStates().query(aid1.prefix, '1'), + await client2.keyStates().query(aid3.prefix, '0'), + await client3.keyStates().query(aid1.prefix, '1'), + await client3.keyStates().query(aid2.prefix, '1'), + ]); + + const [aid2State, aid3State, aid1State] = await Promise.all([ + waitOperation(client1, updates[0]), + waitOperation(client1, updates[1]), + waitOperation(client2, updates[2]), + waitOperation(client2, updates[3]), + waitOperation(client3, updates[4]), + waitOperation(client3, updates[5]), + ]); + + const states = [aid1State.response, aid2State.response]; + const rstates = [...states, aid3State.response]; + const rotateOperation1 = await client1 + .identifiers() + .rotate(nameMultisig, { states, rstates }); + + const serder1 = rotateOperation1.serder; + const sigers = rotateOperation1.sigs.map( + (sig) => new signify.Siger({ qb64: sig }) + ); + const ims = signify.d(signify.messagize(serder1, sigers)); + const atc = ims.substring(serder1.size); + const rembeds = { + rot: [serder1, atc], + }; + const smids = states.map((state) => state['i']); + const rmids = rstates.map((state) => state['i']); + const recp = [aid2.state, aid3.state].map((state) => state['i']); + + await client1 + .exchanges() + .send( + 'member1', + 'multisig', + aid1, + '/multisig/rot', + { gid: serder1.pre, smids, rmids }, + rembeds, + recp + ); + + const rotationNotification = await waitAndMarkNotification( + client3, + '/multisig/rot' + ); + + const response = await client3 + .groups() + .getRequest(rotationNotification); + const exn = response[0].exn; + + const serder3 = new Serder(exn.e.rot); + + const keeper = await client3.manager!.get(aid3); + const sigs = keeper.sign(serder3.raw); + + const joinOperation = await client3 + .groups() + .join( + nameMultisig, + serder3, + sigs, + exn.a?.gid as string, + smids, + rmids + ); + + await waitOperation(client3, joinOperation); + }); +}); + +async function createAID(client: SignifyClient, name: string, wits: string[]) { + await getOrCreateIdentifier(client, name, { + wits: wits, + toad: wits.length, + }); + return await client.identifiers().get(name); +} + +async function waitAndMarkNotification(client: SignifyClient, route: string) { + const notes = await waitForNotifications(client, route); + + await Promise.all( + notes.map(async (note) => { + await markNotification(client, note); + }) + ); + + return notes[notes.length - 1]?.a.d ?? ''; +} diff --git a/examples/integration-scripts/utils/test-setup.ts b/examples/integration-scripts/utils/test-setup.ts index ea9760f1..4a94f426 100644 --- a/examples/integration-scripts/utils/test-setup.ts +++ b/examples/integration-scripts/utils/test-setup.ts @@ -33,7 +33,6 @@ export async function getOrCreateClients( ); } const clients: SignifyClient[] = await Promise.all(tasks); - console.log(`SIGNIFY_SECRETS="${clients.map((i) => i.bran).join(',')}"`); return clients; } @@ -55,10 +54,6 @@ export async function getOrCreateClient( if (!res.ok) throw new Error(); await client.connect(); } - console.log('client', { - agent: client.agent?.pre, - controller: client.controller.pre, - }); return client; } @@ -80,7 +75,6 @@ export async function getOrCreateIdentifier( let id: any = undefined; try { const identfier = await client.identifiers().get(name); - // console.log("identifiers.get", identfier); id = identfier.prefix; } catch { const env = resolveEnvironment(); @@ -93,21 +87,18 @@ export async function getOrCreateIdentifier( .create(name, kargs); let op = await result.op(); op = await waitOperation(client, op); - // console.log("identifiers.create", op); id = op.response.i; } - const eid = client.agent?.pre!; - if (!(await hasEndRole(client, name, 'agent', eid))) { + const eid = client.agent?.pre; + if (!(await hasEndRole(client, name, 'agent', eid!))) { const result: EventResult = await client .identifiers() .addEndRole(name, 'agent', eid); - let op = await result.op(); - op = await waitOperation(client, op); - // console.log("identifiers.addEndRole", op); + const op = await result.op(); + await waitOperation(client, op); } const oobi = await client.oobis().get(name, 'agent'); const result: [string, string] = [id, oobi.oobis[0]]; - console.log(name, result); return result; } @@ -126,7 +117,6 @@ export async function getEndRoles( const response: Response = await client.fetch(path, 'GET', null); if (!response.ok) throw new Error(await response.text()); const result = await response.json(); - // console.log("getEndRoles", result); return result; } @@ -163,16 +153,13 @@ export async function getOrCreateContact( oobi: string ): Promise { const list = await client.contacts().list(undefined, 'alias', `^${name}$`); - // console.log("contacts.list", list); if (list.length > 0) { const contact = list[0]; if (contact.oobi === oobi) { - // console.log("contacts.id", contact.id); return contact.id; } } let op = await client.oobis().resolve(oobi, name); op = await waitOperation(client, op); - // console.log("oobis.resolve", op); return op.response.i; } diff --git a/examples/integration-scripts/utils/test-util.ts b/examples/integration-scripts/utils/test-util.ts index 4a08262b..9d0ee81e 100644 --- a/examples/integration-scripts/utils/test-util.ts +++ b/examples/integration-scripts/utils/test-util.ts @@ -114,12 +114,10 @@ export async function waitOperation( if (t.done !== true) { throw new Error(`Operation ${name} not done`); } - console.log('DONE', name); return t; }, options); let i: Operation | undefined = result; while (i !== undefined) { - // console.log('DELETE', i.name); await client.operations().delete(i.name); i = i.metadata?.depends; }