From 361325e89cabb937324fc30f9346916cf0835032 Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Thu, 7 Sep 2023 10:18:42 -0300 Subject: [PATCH 1/7] multisig with notif --- examples/integration-scripts/multisig.ts | 70 +++++++++++++++++------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/examples/integration-scripts/multisig.ts b/examples/integration-scripts/multisig.ts index 9f185cd1..8ea2d0a7 100644 --- a/examples/integration-scripts/multisig.ts +++ b/examples/integration-scripts/multisig.ts @@ -131,7 +131,7 @@ async function run() { } console.log("Multisig3 resolved 2 OOBIs") - // Create a multisig identifier + // First member start the creation of a multisig identifier let rstates = [aid1["state"], aid2["state"], aid3["state"]] let states = rstates icpResult1 = client1.identifiers().create("multisig",{ @@ -163,18 +163,33 @@ async function run() { await client1.exchanges().send("multisig1", "multisig", aid1, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Multisig1 joined multisig waiting for others...") + console.log("Multisig1 create multisig, waiting for others...") + // Second member check notifications and join the multisig + let msgSaid = "" + while (msgSaid=="") { + let notifications = await client2.notifications().list() + for (let notif of notifications.notes){ + if (notif.a.r == '/multisig/icp') { + msgSaid = notif.a.d + await client2.notifications().mark(notif.i) + console.log("Multisig2 received request to join multisig") + } + } + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + let res = await client2.groups().getRequest(msgSaid) + let exn = res[0].exn + let icp = exn.e.icp + icpResult2 = client2.identifiers().create("multisig",{ algo: signify.Algos.group, mhab: aid2, - isith: 3, - nsith: 3, - toad: 3, - wits: [ - "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"], + isith: icp.kt, + nsith: icp.nt, + toad: parseInt(icp.bt), + wits: icp.b, states: states, rstates: rstates }) @@ -189,23 +204,38 @@ async function run() { icp: [serder, atc], } - smids = states.map((state) => state['i']) + smids = exn.a.smids recp = [aid1["state"], aid3["state"]].map((state) => state['i']) await client2.exchanges().send("multisig2", "multisig", aid2, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Multisig2 joined multisig waiting for others...") + console.log("Multisig2 joined multisig, waiting for others...") + + // Third member check notifications and join the multisig + msgSaid = "" + while (msgSaid=="") { + let notifications = await client3.notifications().list() + for (let notif of notifications.notes){ + if (notif.a.r == '/multisig/icp') { + msgSaid = notif.a.d + await client3.notifications().mark(notif.i) + console.log("Multisig3 received request to join multisig") + } + } + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + res = await client3.groups().getRequest(msgSaid) + exn = res[0].exn + icp = exn.e.icp icpResult3 = client3.identifiers().create("multisig",{ algo: signify.Algos.group, mhab: aid3, - isith: 3, - nsith: 3, - toad: 3, - wits: [ - "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"], + isith: icp.kt, + nsith: icp.nt, + toad: parseInt(icp.bt), + wits: icp.b, states: states, rstates: rstates }) @@ -220,12 +250,12 @@ async function run() { icp: [serder, atc], } - smids = states.map((state) => state['i']) + smids = exn.a.smids recp = [aid1["state"], aid2["state"]].map((state) => state['i']) await client3.exchanges().send("multisig3", "multisig", aid3, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Multisig3 joined multisig waiting for others...") + console.log("Multisig3 joined, multisig waiting for others...") while (!op1["done"]) { op1 = await client1.operations().get(op1.name); From c9adaecafb2864c02d4cdcbbd7b6d647943a1581 Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Thu, 7 Sep 2023 15:46:30 -0300 Subject: [PATCH 2/7] EventResponse on rot and ixn --- examples/integration-scripts/salty.ts | 6 ++++-- src/keri/app/signify.ts | 31 +++++++++++++-------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/examples/integration-scripts/salty.ts b/examples/integration-scripts/salty.ts index 4e1082cd..cd39a643 100644 --- a/examples/integration-scripts/salty.ts +++ b/examples/integration-scripts/salty.ts @@ -89,7 +89,8 @@ async function run() { aid = aids.aids[0] assert.equal(aid.name, 'aid3') - op = await client1.identifiers().rotate('aid1') + icpResult = await client1.identifiers().rotate('aid1') + op = await icpResult.op() assert.equal(op['done'], true) let ked = op['response'] let rot = new signify.Serder(ked) @@ -100,7 +101,8 @@ async function run() { assert.equal(rot.verfers[0].qb64, 'DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly') assert.equal(rot.digers[0].qb64, 'EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk') - op = await client1.identifiers().interact("aid1", [icp.pre]) + icpResult = await client1.identifiers().interact("aid1", [icp.pre]) + op = await icpResult.op() assert.equal(op['done'], true) ked = op['response'] let ixn = new signify.Serder(ked) diff --git a/src/keri/app/signify.ts b/src/keri/app/signify.ts index b7243434..43ac4178 100644 --- a/src/keri/app/signify.ts +++ b/src/keri/app/signify.ts @@ -24,7 +24,7 @@ export class CredentialTypes { static received = "received" } -/** Starte of the client */ +/** State of the client */ class State { agent: any | null controller: any | null @@ -430,7 +430,7 @@ export class SignifyClient { } /** - * Get groups resource + * Get exchange resource * @returns {Exchanges} */ exchanges(): Exchanges { @@ -479,7 +479,7 @@ export interface RotateIdentifierArgs { rstates?: any[] } -export class InceptionResult { +export class EventResult { private readonly _serder: Serder private readonly _sigs: string[] private readonly promise: Promise @@ -562,9 +562,9 @@ export class Identifier { * @async * @param {string} name Name or alias of the identifier * @param {CreateIdentiferArgs} [kargs] Optional parameters to create the identifier - * @returns {InceptionResult} The inception result + * @returns {EventResult} The inception result */ - create(name: string, kargs:CreateIdentiferArgs={}): InceptionResult { + create(name: string, kargs:CreateIdentiferArgs={}): EventResult { const algo = kargs.algo == undefined ? Algos.salty : kargs.algo @@ -666,7 +666,7 @@ export class Identifier { this.client.pidx = this.client.pidx + 1 let res = this.client.fetch("/identifiers", "POST", jsondata) - return new InceptionResult(serder, sigs, res) + return new EventResult(serder, sigs, res) } /** @@ -674,9 +674,9 @@ export class Identifier { * @async * @param {string} name Name or alias of the identifier * @param {any} [data] Option data to be anchored in the interaction event - * @returns {Promise} A promise to the long-running operation + * @returns {Promise} A promise to the interaction event result */ - async interact(name: string, data?: any): Promise { + async interact(name: string, data?: any): Promise { let hab = await this.get(name) let pre: string = hab.prefix @@ -697,8 +697,8 @@ export class Identifier { } jsondata[keeper.algo] = keeper.params() - let res = await this.client.fetch("/identifiers/" + name + "?type=ixn", "PUT", jsondata) - return await res.json() + let res = this.client.fetch("/identifiers/" + name + "?type=ixn", "PUT", jsondata) + return new EventResult(serder, sigs, res) } @@ -706,15 +706,14 @@ export class Identifier { * Generate a rotation event in a managed identifier * @param {string} name Name or alias of the identifier * @param {RotateIdentifierArgs} [kargs] Optional parameters requiered to generate the rotation event - * @returns {Promise} + * @returns {Promise} A promise to the rotation event result */ - async rotate(name: string, kargs: RotateIdentifierArgs={}): Promise { + async rotate(name: string, kargs: RotateIdentifierArgs={}): Promise { let transferable = kargs.transferable ?? true let ncode = kargs.ncode ?? MtrDex.Ed25519_Seed let ncount = kargs.ncount ?? 1 - let hab = await this.get(name) let pre = hab.prefix @@ -774,8 +773,8 @@ export class Identifier { } jsondata[keeper.algo] = keeper.params() - let res = await this.client.fetch("/identifiers/" + name, "PUT", jsondata) - return await res.json() + let res = this.client.fetch("/identifiers/" + name, "PUT", jsondata) + return new EventResult(serder, sigs, res) } /** @@ -1938,7 +1937,7 @@ export class Exchanges { } /** - * Send exn messaget to list of recipients + * Send exn messages to list of recipients * @async * @returns {Promise} A promise to the list of replay messages * @param name From 3c03046be372004cb3160fcf82c6d0c1b9f89e6a Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Thu, 7 Sep 2023 15:48:08 -0300 Subject: [PATCH 3/7] randy and salty --- examples/integration-scripts/randy.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/integration-scripts/randy.ts b/examples/integration-scripts/randy.ts index 0ed53017..2b43323e 100644 --- a/examples/integration-scripts/randy.ts +++ b/examples/integration-scripts/randy.ts @@ -44,7 +44,8 @@ async function run() { assert.equal(aid.name, 'aid1') assert.equal(aid.prefix, icp.pre) - op = await client1.identifiers().interact("aid1", [icp.pre]) + icpResult = await client1.identifiers().interact("aid1", [icp.pre]) + op = await icpResult.op() assert.equal(op['done'], true) let ked = op['response'] let ixn = new signify.Serder(ked) @@ -59,7 +60,8 @@ async function run() { let log = await events.get(aid["prefix"]) assert.equal(log.length, 2) - op = await client1.identifiers().rotate('aid1') + icpResult = await client1.identifiers().rotate('aid1') + op = await icpResult.op() assert.equal(op['done'], true) ked = op['response'] let rot = new signify.Serder(ked) From d4db431cb239ee75cee0a32c2cdcc7e60260f112 Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Thu, 7 Sep 2023 22:12:21 -0300 Subject: [PATCH 4/7] mulltisig interaction --- examples/integration-scripts/challenge.ts | 6 +- examples/integration-scripts/multisig.ts | 196 +++++++++++++++++----- 2 files changed, 158 insertions(+), 44 deletions(-) diff --git a/examples/integration-scripts/challenge.ts b/examples/integration-scripts/challenge.ts index 0c9215e0..e5f7ecd1 100644 --- a/examples/integration-scripts/challenge.ts +++ b/examples/integration-scripts/challenge.ts @@ -40,13 +40,14 @@ async function run() { assert.equal(challenge1_big.words.length, 24) // Create two identifiers, one for each client - let op1 = await client1.identifiers().create('alice', { + let icpResult1 = await client1.identifiers().create('alice', { toad: 3, wits: [ "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"] }) + let op1 = await icpResult1.op() while (!op1["done"] ) { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -55,13 +56,14 @@ async function run() { await client1.identifiers().addEndRole("alice", 'agent', client1!.agent!.pre) console.log("Alice's AID:", aid1.i) - let op2 = await client2.identifiers().create('bob', { + let icpResult2 = await client2.identifiers().create('bob', { toad: 3, wits: [ "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"] }) + let op2 = await icpResult2.op() while (!op2["done"] ) { op2 = await client2.operations().get(op2.name); await new Promise(resolve => setTimeout(resolve, 1000)); diff --git a/examples/integration-scripts/multisig.ts b/examples/integration-scripts/multisig.ts index 8ea2d0a7..ed4ece69 100644 --- a/examples/integration-scripts/multisig.ts +++ b/examples/integration-scripts/multisig.ts @@ -40,8 +40,8 @@ async function run() { console.log("Client 3 connected. Client AID:",state3.controller.state.i,"Agent AID: ", state3.agent.i) - // Create two identifiers, one for each client - let icpResult1 = client1.identifiers().create('multisig1', { + // Create three identifiers, one for each client + let icpResult1 = client1.identifiers().create('member1', { toad: 3, wits: [ "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", @@ -53,11 +53,11 @@ async function run() { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid1 = await client1.identifiers().get("multisig1") - await client1.identifiers().addEndRole("multisig1", 'agent', client1!.agent!.pre) - console.log("Multisig1's AID:", aid1.prefix) + const aid1 = await client1.identifiers().get("member1") + await client1.identifiers().addEndRole("member1", 'agent', client1!.agent!.pre) + console.log("Member1's AID:", aid1.prefix) - let icpResult2 = client2.identifiers().create('multisig2', { + let icpResult2 = client2.identifiers().create('member2', { toad: 3, wits: [ "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", @@ -69,11 +69,11 @@ async function run() { op2 = await client2.operations().get(op2.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid2 = await client2.identifiers().get("multisig2") - await client2.identifiers().addEndRole("multisig2", 'agent', client2!.agent!.pre) - console.log("Multisig2's AID:", aid2.prefix) + const aid2 = await client2.identifiers().get("member2") + await client2.identifiers().addEndRole("member2", 'agent', client2!.agent!.pre) + console.log("Member2's AID:", aid2.prefix) - let icpResult3 = client3.identifiers().create('multisig3', { + let icpResult3 = client3.identifiers().create('member3', { toad: 3, wits: [ "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", @@ -85,51 +85,51 @@ async function run() { op3 = await client3.operations().get(op3.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid3 = await client3.identifiers().get("multisig3") - await client3.identifiers().addEndRole("multisig3", 'agent', client3!.agent!.pre) - console.log("Multisig3's AID:", aid3.prefix) + const aid3 = await client3.identifiers().get("member3") + await client3.identifiers().addEndRole("member3", 'agent', client3!.agent!.pre) + console.log("Member3's AID:", aid3.prefix) // Exchenge OOBIs console.log("Resolving OOBIs") - let oobi1 = await client1.oobis().get("multisig1","agent") - let oobi2 = await client2.oobis().get("multisig2","agent") - let oobi3 = await client3.oobis().get("multisig3","agent") + let oobi1 = await client1.oobis().get("member1","agent") + let oobi2 = await client2.oobis().get("member2","agent") + let oobi3 = await client3.oobis().get("member3","agent") - op1 = await client1.oobis().resolve(oobi2.oobis[0],"multisig2") + op1 = await client1.oobis().resolve(oobi2.oobis[0],"member2") while (!op1["done"]) { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - op1 = await client1.oobis().resolve(oobi3.oobis[0],"multisig3") + op1 = await client1.oobis().resolve(oobi3.oobis[0],"member3") while (!op1["done"]) { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - console.log("Multisig1 resolved 2 OOBIs") + console.log("Member1 resolved 2 OOBIs") - op2 = await client2.oobis().resolve(oobi1.oobis[0],"multisig1") + op2 = await client2.oobis().resolve(oobi1.oobis[0],"member1") while (!op2["done"]) { op2 = await client2.operations().get(op2.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - op2 = await client2.oobis().resolve(oobi3.oobis[0],"multisig3") + op2 = await client2.oobis().resolve(oobi3.oobis[0],"member3") while (!op2["done"]) { op2 = await client2.operations().get(op2.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - console.log("Multisig2 resolved 2 OOBIs") + console.log("Member2 resolved 2 OOBIs") - op3 = await client3.oobis().resolve(oobi1.oobis[0],"multisig1") + op3 = await client3.oobis().resolve(oobi1.oobis[0],"member1") while (!op3["done"]) { op3 = await client3.operations().get(op3.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - op3 = await client3.oobis().resolve(oobi2.oobis[0],"multisig2") + op3 = await client3.oobis().resolve(oobi2.oobis[0],"member2") while (!op3["done"]) { op3 = await client3.operations().get(op3.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - console.log("Multisig3 resolved 2 OOBIs") + console.log("Member3 resolved 2 OOBIs") // First member start the creation of a multisig identifier let rstates = [aid1["state"], aid2["state"], aid3["state"]] @@ -161,9 +161,9 @@ async function run() { let smids = states.map((state) => state['i']) let recp = [aid2["state"], aid3["state"]].map((state) => state['i']) - await client1.exchanges().send("multisig1", "multisig", aid1, "/multisig/icp", + await client1.exchanges().send("member1", "multisig", aid1, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Multisig1 create multisig, waiting for others...") + console.log("Member1 create multisig, waiting for others...") // Second member check notifications and join the multisig let msgSaid = "" @@ -173,7 +173,7 @@ async function run() { if (notif.a.r == '/multisig/icp') { msgSaid = notif.a.d await client2.notifications().mark(notif.i) - console.log("Multisig2 received request to join multisig") + console.log("Member2 received request to join multisig") } } await new Promise(resolve => setTimeout(resolve, 1000)); @@ -207,9 +207,9 @@ async function run() { smids = exn.a.smids recp = [aid1["state"], aid3["state"]].map((state) => state['i']) - await client2.exchanges().send("multisig2", "multisig", aid2, "/multisig/icp", + await client2.exchanges().send("member2", "multisig", aid2, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Multisig2 joined multisig, waiting for others...") + console.log("Member2 joined multisig, waiting for others...") // Third member check notifications and join the multisig @@ -220,7 +220,7 @@ async function run() { if (notif.a.r == '/multisig/icp') { msgSaid = notif.a.d await client3.notifications().mark(notif.i) - console.log("Multisig3 received request to join multisig") + console.log("Member3 received request to join multisig") } } await new Promise(resolve => setTimeout(resolve, 1000)); @@ -253,9 +253,9 @@ async function run() { smids = exn.a.smids recp = [aid1["state"], aid2["state"]].map((state) => state['i']) - await client3.exchanges().send("multisig3", "multisig", aid3, "/multisig/icp", + await client3.exchanges().send("member3", "multisig", aid3, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Multisig3 joined, multisig waiting for others...") + console.log("Member3 joined, multisig waiting for others...") while (!op1["done"]) { op1 = await client1.operations().get(op1.name); @@ -273,21 +273,133 @@ async function run() { const identifiers1 = await client1.identifiers().list() assert.equal(identifiers1.aids.length, 2) - assert.equal(identifiers1.aids[0].name, "multisig") - assert.equal(identifiers1.aids[1].name, "multisig1") + assert.equal(identifiers1.aids[0].name, "member1") + assert.equal(identifiers1.aids[1].name, "multisig") const identifiers2 = await client2.identifiers().list() assert.equal(identifiers2.aids.length, 2) - assert.equal(identifiers2.aids[0].name, "multisig") - assert.equal(identifiers2.aids[1].name, "multisig2") + assert.equal(identifiers2.aids[0].name, "member2") + assert.equal(identifiers2.aids[1].name, "multisig") const identifiers3 = await client3.identifiers().list() assert.equal(identifiers3.aids.length, 2) - assert.equal(identifiers3.aids[0].name, "multisig") - assert.equal(identifiers3.aids[1].name, "multisig3") + assert.equal(identifiers3.aids[0].name, "member3") + assert.equal(identifiers3.aids[1].name, "multisig") - console.log("Client 1 identifiers:", identifiers1.aids[0].name, identifiers1.aids[1].name) - console.log("Client 2 identifiers:", identifiers2.aids[0].name, identifiers2.aids[1].name) - console.log("Client 3 identifiers:", identifiers3.aids[0].name, identifiers3.aids[1].name) + console.log("Client 1 managed AIDs:", identifiers1.aids[0].name, identifiers1.aids[1].name) + console.log("Client 2 managed AIDs:", identifiers2.aids[0].name, identifiers2.aids[1].name) + console.log("Client 3 managed AIDs:", identifiers3.aids[0].name, identifiers3.aids[1].name) + + // Join an interaction event with the group + + // Member1 propose an interaction event + let data = {"i": "EE77q3_zWb5ojgJr-R1vzsL5yiL4Nzm-bfSOQzQl02dy"} + let eventResponse1 = await client1.identifiers().interact("multisig", data) + op1 = await eventResponse1.op() + serder = eventResponse1.serder + sigs = eventResponse1.sigs + sigers = sigs.map((sig: any) => new signify.Siger({qb64: sig})) + + ims = signify.d(signify.messagize(serder, sigers)) + atc = ims.substring(serder.size) + let xembeds = { + ixn: [serder, atc], + } + + smids = states.map((state) => state['i']) + recp = [aid2["state"], aid3["state"]].map((state) => state['i']) + + await client1.exchanges().send("member1", "multisig", aid1, "/multisig/ixn", + {'gid': serder.pre, smids: smids, rmids: smids}, xembeds, recp) + console.log("Member1 initiates interaction event, waiting for others...") + + // Member2 check for notifications and join the interaction event + msgSaid = "" + while (msgSaid=="") { + let notifications = await client2.notifications().list() + for (let notif of notifications.notes){ + if (notif.a.r == '/multisig/ixn') { + msgSaid = notif.a.d + await client2.notifications().mark(notif.i) + console.log("Member2 received request to join the interaction event") + } + } + await new Promise(resolve => setTimeout(resolve, 1000)); + } + res = await client2.groups().getRequest(msgSaid) + exn = res[0].exn + let ixn = exn.e.ixn + data = ixn.a + + icpResult2 = await client2.identifiers().interact("multisig",data) + op2 = await icpResult2.op() + serder = icpResult2.serder + sigs = icpResult2.sigs + sigers = sigs.map((sig: any) => new signify.Siger({qb64: sig})) + + ims = signify.d(signify.messagize(serder, sigers)) + atc = ims.substring(serder.size) + xembeds = { + ixn: [serder, atc], + } + + smids = exn.a.smids + recp = [aid1["state"], aid3["state"]].map((state) => state['i']) + + await client2.exchanges().send("member2", "multisig", aid2, "/multisig/ixn", + {'gid': serder.pre, smids: smids, rmids: smids}, xembeds, recp) + console.log("Member2 joins interaction event, waiting for others...") + + // Member3 check for notifications and join the interaction event + msgSaid = "" + while (msgSaid=="") { + let notifications = await client3.notifications().list() + for (let notif of notifications.notes){ + if (notif.a.r == '/multisig/ixn') { + msgSaid = notif.a.d + await client3.notifications().mark(notif.i) + console.log("Member3 received request to join the interaction event") + } + } + await new Promise(resolve => setTimeout(resolve, 1000)); + } + res = await client3.groups().getRequest(msgSaid) + exn = res[0].exn + ixn = exn.e.ixn + data = ixn.a + + icpResult3 = await client3.identifiers().interact("multisig",data) + op3 = await icpResult3.op() + serder = icpResult3.serder + sigs = icpResult3.sigs + sigers = sigs.map((sig: any) => new signify.Siger({qb64: sig})) + + ims = signify.d(signify.messagize(serder, sigers)) + atc = ims.substring(serder.size) + xembeds = { + ixn: [serder, atc], + } + + smids = exn.a.smids + recp = [aid1["state"], aid2["state"]].map((state) => state['i']) + + await client3.exchanges().send("member3", "multisig", aid3, "/multisig/ixn", + {'gid': serder.pre, smids: smids, rmids: smids}, xembeds, recp) + console.log("Member3 joins interaction event, waiting for others...") + + // Check for completion + while (!op1["done"]) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + while (!op2["done"]) { + op2 = await client2.operations().get(op2.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + while (!op3["done"]) { + op3 = await client3.operations().get(op3.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + console.log("Multisig interaction comlpeted!") } \ No newline at end of file From 536c3b995cecbbb57ea2be56d9693cd88123ccb5 Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Sat, 9 Sep 2023 16:11:37 -0300 Subject: [PATCH 5/7] multisig with notif --- examples/integration-scripts/multisig.ts | 221 +++++++++++++++--- .../integration-scripts/tsconfig.node.json | 1 + examples/integration-scripts/witness.ts | 30 ++- src/keri/core/eventing.ts | 7 +- 4 files changed, 212 insertions(+), 47 deletions(-) diff --git a/examples/integration-scripts/multisig.ts b/examples/integration-scripts/multisig.ts index ed4ece69..addcc93d 100644 --- a/examples/integration-scripts/multisig.ts +++ b/examples/integration-scripts/multisig.ts @@ -1,24 +1,13 @@ import { strict as assert } from "assert"; +import signify from "signify-ts"; -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting MULTISIG test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot three clients const bran1 = signify.randomPasscode() const bran2 = signify.randomPasscode() @@ -53,7 +42,7 @@ async function run() { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid1 = await client1.identifiers().get("member1") + let aid1 = await client1.identifiers().get("member1") await client1.identifiers().addEndRole("member1", 'agent', client1!.agent!.pre) console.log("Member1's AID:", aid1.prefix) @@ -69,7 +58,7 @@ async function run() { op2 = await client2.operations().get(op2.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid2 = await client2.identifiers().get("member2") + let aid2 = await client2.identifiers().get("member2") await client2.identifiers().addEndRole("member2", 'agent', client2!.agent!.pre) console.log("Member2's AID:", aid2.prefix) @@ -85,11 +74,11 @@ async function run() { op3 = await client3.operations().get(op3.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid3 = await client3.identifiers().get("member3") + let aid3 = await client3.identifiers().get("member3") await client3.identifiers().addEndRole("member3", 'agent', client3!.agent!.pre) console.log("Member3's AID:", aid3.prefix) - // Exchenge OOBIs + // Exchange OOBIs console.log("Resolving OOBIs") let oobi1 = await client1.oobis().get("member1","agent") let oobi2 = await client2.oobis().get("member2","agent") @@ -163,7 +152,7 @@ async function run() { await client1.exchanges().send("member1", "multisig", aid1, "/multisig/icp", {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) - console.log("Member1 create multisig, waiting for others...") + console.log("Member1 initiated multisig, waiting for others to join...") // Second member check notifications and join the multisig let msgSaid = "" @@ -173,7 +162,7 @@ async function run() { if (notif.a.r == '/multisig/icp') { msgSaid = notif.a.d await client2.notifications().mark(notif.i) - console.log("Member2 received request to join multisig") + console.log("Member2 received exchange message to join multisig") } } await new Promise(resolve => setTimeout(resolve, 1000)); @@ -220,7 +209,7 @@ async function run() { if (notif.a.r == '/multisig/icp') { msgSaid = notif.a.d await client3.notifications().mark(notif.i) - console.log("Member3 received request to join multisig") + console.log("Member3 received exchange message to join multisig") } } await new Promise(resolve => setTimeout(resolve, 1000)); @@ -257,6 +246,7 @@ async function run() { {'gid': serder.pre, smids: smids, rmids: smids}, embeds, recp) console.log("Member3 joined, multisig waiting for others...") + // Check for completion while (!op1["done"]) { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -291,9 +281,9 @@ async function run() { console.log("Client 3 managed AIDs:", identifiers3.aids[0].name, identifiers3.aids[1].name) - // Join an interaction event with the group + // MultiSig Interaction - // Member1 propose an interaction event + // Member1 initiates an interaction event let data = {"i": "EE77q3_zWb5ojgJr-R1vzsL5yiL4Nzm-bfSOQzQl02dy"} let eventResponse1 = await client1.identifiers().interact("multisig", data) op1 = await eventResponse1.op() @@ -312,7 +302,7 @@ async function run() { await client1.exchanges().send("member1", "multisig", aid1, "/multisig/ixn", {'gid': serder.pre, smids: smids, rmids: smids}, xembeds, recp) - console.log("Member1 initiates interaction event, waiting for others...") + console.log("Member1 initiates interaction event, waiting for others to join...") // Member2 check for notifications and join the interaction event msgSaid = "" @@ -322,7 +312,7 @@ async function run() { if (notif.a.r == '/multisig/ixn') { msgSaid = notif.a.d await client2.notifications().mark(notif.i) - console.log("Member2 received request to join the interaction event") + console.log("Member2 received exchange message to join the interaction event") } } await new Promise(resolve => setTimeout(resolve, 1000)); @@ -359,7 +349,7 @@ async function run() { if (notif.a.r == '/multisig/ixn') { msgSaid = notif.a.d await client3.notifications().mark(notif.i) - console.log("Member3 received request to join the interaction event") + console.log("Member3 received exchange message to join the interaction event") } } await new Promise(resolve => setTimeout(resolve, 1000)); @@ -401,5 +391,182 @@ async function run() { op3 = await client3.operations().get(op3.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - console.log("Multisig interaction comlpeted!") + console.log("Multisig interaction completed!") + + // Members agree out of band to rotate keys + console.log("Members agree out of band to rotate keys") + icpResult1 = await client1.identifiers().rotate('member1') + op1 = await icpResult1.op() + while (!op1["done"]) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + aid1 = await client1.identifiers().get("member1") + + console.log("Member1 rotated keys") + icpResult2 = await client2.identifiers().rotate('member2') + op2 = await icpResult2.op() + while (!op2["done"]) { + op2 = await client2.operations().get(op2.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + aid2 = await client2.identifiers().get("member2") + console.log("Member2 rotated keys") + icpResult3 = await client3.identifiers().rotate('member3') + op3 = await icpResult3.op() + while (!op3["done"]) { + op3 = await client3.operations().get(op3.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + aid3 = await client3.identifiers().get("member3") + console.log("Member3 rotated keys") + + // Update new key states + op1 = await client1.keyStates().query(aid2.prefix,1) + while (!op1["done"]) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + let aid2State = op1["response"] + op1 = await client1.keyStates().query(aid3.prefix,1) + while (!op1["done"]) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + let aid3State = op1["response"] + + op2 = await client2.keyStates().query(aid3.prefix,1) + while (!op2["done"]) { + op2 = await client2.operations().get(op2.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + op2 = await client2.keyStates().query(aid1.prefix,1) + while (!op2["done"]) { + op2 = await client2.operations().get(op2.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + let aid1State = op2["response"] + + op3 = await client3.keyStates().query(aid1.prefix,1) + while (!op3["done"]) { + op3 = await client3.operations().get(op3.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + op3 = await client3.keyStates().query(aid2.prefix,1) + while (!op3["done"]) { + op3 = await client3.operations().get(op3.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + rstates = [aid1State, aid2State, aid3State] + states = rstates + + // Multisig Rotation + + // Member1 initiates a rotation event + eventResponse1 = await client1.identifiers().rotate("multisig",{states: states,rstates: rstates}) + op1 = await eventResponse1.op() + serder = eventResponse1.serder + sigs = eventResponse1.sigs + sigers = sigs.map((sig: any) => new signify.Siger({qb64: sig})) + + ims = signify.d(signify.messagize(serder, sigers)) + atc = ims.substring(serder.size) + let rembeds = { + rot: [serder, atc], + } + + smids = states.map((state) => state['i']) + recp = [aid2State, aid3State].map((state) => state['i']) + + await client1.exchanges().send("member1", "multisig", aid1 , "/multisig/rot", + {'gid': serder.pre, smids: smids, rmids: smids}, rembeds, recp) + console.log("Member1 initiates rotation event, waiting for others to join...") + + // Member2 check for notifications and join the rotation event + msgSaid = "" + while (msgSaid=="") { + let notifications = await client2.notifications().list() + for (let notif of notifications.notes){ + if (notif.a.r == '/multisig/rot') { + msgSaid = notif.a.d + await client2.notifications().mark(notif.i) + console.log("Member2 received exchange message to join the rotation event") + } + } + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + await new Promise(resolve => setTimeout(resolve, 5000)); + res = await client2.groups().getRequest(msgSaid) + exn = res[0].exn + + icpResult2 = await client2.identifiers().rotate("multisig",{states: states,rstates: rstates}) + op2 = await icpResult2.op() + serder = icpResult2.serder + sigs = icpResult2.sigs + sigers = sigs.map((sig: any) => new signify.Siger({qb64: sig})) + + ims = signify.d(signify.messagize(serder, sigers)) + atc = ims.substring(serder.size) + rembeds = { + rot: [serder, atc], + } + + smids = exn.a.smids + recp = [aid1State, aid3State].map((state) => state['i']) + + await client2.exchanges().send("member2", "multisig", aid2, "/multisig/ixn", + {'gid': serder.pre, smids: smids, rmids: smids}, rembeds, recp) + console.log("Member2 joins rotation event, waiting for others...") + + // Member3 check for notifications and join the rotation event + msgSaid = "" + while (msgSaid=="") { + let notifications = await client3.notifications().list(1) + for (let notif of notifications.notes){ + if (notif.a.r == '/multisig/rot') { + msgSaid = notif.a.d + await client3.notifications().mark(notif.i) + console.log("Member3 received exchange message to join the rotation event") + } + } + await new Promise(resolve => setTimeout(resolve, 1000)); + } + res = await client3.groups().getRequest(msgSaid) + exn = res[0].exn + + icpResult3 = await client3.identifiers().rotate("multisig",{states: states,rstates: rstates}) + op3 = await icpResult3.op() + serder = icpResult3.serder + sigs = icpResult3.sigs + sigers = sigs.map((sig: any) => new signify.Siger({qb64: sig})) + + ims = signify.d(signify.messagize(serder, sigers)) + atc = ims.substring(serder.size) + rembeds = { + rot: [serder, atc], + } + + smids = exn.a.smids + recp = [aid1State, aid2State].map((state) => state['i']) + + await client3.exchanges().send("member3", "multisig", aid3, "/multisig/ixn", + {'gid': serder.pre, smids: smids, rmids: smids}, rembeds, recp) + console.log("Member3 joins rotation event, waiting for others...") + + // Check for completion + while (!op1["done"]) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + while (!op2["done"]) { + op2 = await client2.operations().get(op2.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + while (!op3["done"]) { + op3 = await client3.operations().get(op3.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + console.log("Multisig rotation completed!") } \ No newline at end of file diff --git a/examples/integration-scripts/tsconfig.node.json b/examples/integration-scripts/tsconfig.node.json index 61c424ba..b1035cbd 100644 --- a/examples/integration-scripts/tsconfig.node.json +++ b/examples/integration-scripts/tsconfig.node.json @@ -2,6 +2,7 @@ "compilerOptions": { "composite": true, "skipLibCheck": true, + "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true diff --git a/examples/integration-scripts/witness.ts b/examples/integration-scripts/witness.ts index c91dfeec..e192a7b4 100644 --- a/examples/integration-scripts/witness.ts +++ b/examples/integration-scripts/witness.ts @@ -1,25 +1,13 @@ // This scrip also work if you start keria with no config file with witness urls import { strict as assert } from "assert"; +import signify from "signify-ts"; -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting WITNESS test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) - +await run() async function run() { - + await signify.ready() // Boot client const bran1 = signify.randomPasscode() const client1 = new signify.SignifyClient(url, bran1, signify.Tier.low, boot_url); @@ -48,9 +36,19 @@ async function run() { op1 = await client1.operations().get(op1.name); await new Promise(resolve => setTimeout(resolve, 1000)); } - const aid1 = await client1.identifiers().get("aid1") + let aid1 = await client1.identifiers().get("aid1") console.log("AID:",aid1.prefix) assert.equal(aid1.state.b.length, 1) assert.equal(aid1.state.b[0], witness) + icpResult1 = await client1.identifiers().rotate('aid1') + op1 = await icpResult1.op() + while (!op1["done"] ) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + aid1 = await client1.identifiers().get("aid1") + assert.equal(aid1.state.b.length, 1) + assert.equal(aid1.state.b[0], witness) + } \ No newline at end of file diff --git a/src/keri/core/eventing.ts b/src/keri/core/eventing.ts index 32e61ad8..58648b2c 100644 --- a/src/keri/core/eventing.ts +++ b/src/keri/core/eventing.ts @@ -148,10 +148,9 @@ export function rotate({ throw new Error(`Invalid member combination among cuts = ${cuts}, and adds = ${adds}.`) } - let newitsetdiff = new Set([...witset].filter(x => cutset.has(x))) - let newitset = new Set() - newitsetdiff.forEach(newitset.add, newitset) - addset.forEach(newitset.add, newitset) + let newitsetdiff = new Set(_wits) + _cuts.forEach(function(v) {newitsetdiff.delete(v) }) + let newitset = new Set(function*() { yield* newitsetdiff; yield* addset; }()) if (newitset.size != (witset.size - cutset.size + addset.size)) { throw new Error(`Invalid member combination among wits = ${wits}, cuts = ${cuts}, and adds = ${adds}.`) From d3aaa4980e40ad73c0eea2d1dcc6a83398c8857b Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Sat, 9 Sep 2023 16:15:10 -0300 Subject: [PATCH 6/7] fix antipatern --- examples/integration-scripts/challenge.ts | 19 ++++--------------- examples/integration-scripts/credentials.ts | 19 ++++--------------- examples/integration-scripts/delegation.ts | 19 ++++--------------- examples/integration-scripts/multisig.ts | 4 ++-- examples/integration-scripts/randy.ts | 19 ++++--------------- .../integration-scripts/request-present.ts | 19 ++++--------------- examples/integration-scripts/salty.ts | 19 ++++--------------- 7 files changed, 26 insertions(+), 92 deletions(-) diff --git a/examples/integration-scripts/challenge.ts b/examples/integration-scripts/challenge.ts index e5f7ecd1..b8398d42 100644 --- a/examples/integration-scripts/challenge.ts +++ b/examples/integration-scripts/challenge.ts @@ -1,24 +1,13 @@ -import { strict as assert } from "assert"; +import { strict as assert } from "assert" +import signify from "signify-ts" -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting CHALLENGE test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot two clients const bran1 = signify.randomPasscode() const bran2 = signify.randomPasscode() diff --git a/examples/integration-scripts/credentials.ts b/examples/integration-scripts/credentials.ts index 772aea7f..ad28c718 100644 --- a/examples/integration-scripts/credentials.ts +++ b/examples/integration-scripts/credentials.ts @@ -1,24 +1,13 @@ -import { strict as assert } from "assert"; +import { strict as assert } from "assert" +import signify from "signify-ts" -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting CREDENTIALS test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot three clients const bran1 = signify.randomPasscode() const bran2 = signify.randomPasscode() diff --git a/examples/integration-scripts/delegation.ts b/examples/integration-scripts/delegation.ts index 8563fde1..3c6dc821 100644 --- a/examples/integration-scripts/delegation.ts +++ b/examples/integration-scripts/delegation.ts @@ -1,24 +1,13 @@ -import { strict as assert } from "assert"; +import { strict as assert } from "assert" +import signify from "signify-ts" -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting DELEGATION test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot two clients const bran1 = signify.randomPasscode() const bran2 = signify.randomPasscode() diff --git a/examples/integration-scripts/multisig.ts b/examples/integration-scripts/multisig.ts index addcc93d..273ffce8 100644 --- a/examples/integration-scripts/multisig.ts +++ b/examples/integration-scripts/multisig.ts @@ -1,5 +1,5 @@ -import { strict as assert } from "assert"; -import signify from "signify-ts"; +import { strict as assert } from "assert" +import signify from "signify-ts" const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" diff --git a/examples/integration-scripts/randy.ts b/examples/integration-scripts/randy.ts index 2b43323e..caa1ad7a 100644 --- a/examples/integration-scripts/randy.ts +++ b/examples/integration-scripts/randy.ts @@ -1,24 +1,13 @@ -import { strict as assert } from "assert"; +import { strict as assert } from "assert" +import signify from "signify-ts" -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting RANDY test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot client const bran1 = signify.randomPasscode() const client1 = new signify.SignifyClient(url, bran1, signify.Tier.low, boot_url); diff --git a/examples/integration-scripts/request-present.ts b/examples/integration-scripts/request-present.ts index 295e356c..3f4d0523 100644 --- a/examples/integration-scripts/request-present.ts +++ b/examples/integration-scripts/request-present.ts @@ -1,24 +1,13 @@ -import { strict as assert } from "assert"; +import { strict as assert } from "assert" +import signify from "signify-ts" -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting REQUEST-PRESENT test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot three clients const bran1 = signify.randomPasscode() const bran2 = signify.randomPasscode() diff --git a/examples/integration-scripts/salty.ts b/examples/integration-scripts/salty.ts index cd39a643..9a222489 100644 --- a/examples/integration-scripts/salty.ts +++ b/examples/integration-scripts/salty.ts @@ -1,24 +1,13 @@ -import { strict as assert } from "assert"; +import { strict as assert } from "assert" +import signify from "signify-ts" -let signify: any; const url = "http://127.0.0.1:3901" const boot_url = "http://127.0.0.1:3903" -// @ts-ignore -import('signify-ts').then( - (module) => { - signify = module - signify.ready().then(() => { - console.log("*** Starting SALTY test ***"); - run().then(() => { - console.log("*** Test complete ***") - }); - }); - } -) +await run() async function run() { - + await signify.ready() // Boot client const bran1 = signify.randomPasscode() const client1 = new signify.SignifyClient(url, bran1, signify.Tier.low, boot_url); From 2530a0d514e1f20761619f77959fb658f14b5c6e Mon Sep 17 00:00:00 2001 From: Rodolfo Miranda Date: Sat, 9 Sep 2023 17:43:16 -0300 Subject: [PATCH 7/7] witness cuts adds --- examples/integration-scripts/witness.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/examples/integration-scripts/witness.ts b/examples/integration-scripts/witness.ts index e192a7b4..f542df91 100644 --- a/examples/integration-scripts/witness.ts +++ b/examples/integration-scripts/witness.ts @@ -50,5 +50,30 @@ async function run() { aid1 = await client1.identifiers().get("aid1") assert.equal(aid1.state.b.length, 1) assert.equal(aid1.state.b[0], witness) + + // Remove witness + icpResult1 = await client1.identifiers().rotate('aid1',{cuts: [witness]}) + + op1 = await icpResult1.op() + while (!op1["done"] ) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + aid1 = await client1.identifiers().get("aid1") + assert.equal(aid1.state.b.length, 0) + + // Add witness again + + icpResult1 = await client1.identifiers().rotate('aid1',{adds: [witness]}) + + op1 = await icpResult1.op() + while (!op1["done"] ) { + op1 = await client1.operations().get(op1.name); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + aid1 = await client1.identifiers().get("aid1") + assert.equal(aid1.state.b.length, 1) + assert.equal(aid1.state.b.length, 1) + assert.equal(aid1.state.b[0], witness) } \ No newline at end of file