From 12acc532f6dfb39ca764ce191660cbb007b09811 Mon Sep 17 00:00:00 2001 From: lenkan Date: Tue, 21 Nov 2023 14:04:33 +0100 Subject: [PATCH] enable integration tests for multisig.ts and credentials.ts - Adding args and result interfaces for issue and grant methods - Trying to clarify the flow of data --- .../{credentials.ts => credentials.test.ts} | 242 +++++------ examples/integration-scripts/multisig.test.ts | 317 ++++++--------- .../single-issuer-holder.test.ts | 105 ++--- src/keri/app/coring.ts | 10 +- src/keri/app/credentialing.ts | 377 ++++++++---------- src/keri/core/utils.ts | 18 +- test/app/credentialing.test.ts | 46 +-- test/core/utils.test.ts | 8 +- 8 files changed, 484 insertions(+), 639 deletions(-) rename examples/integration-scripts/{credentials.ts => credentials.test.ts} (72%) diff --git a/examples/integration-scripts/credentials.ts b/examples/integration-scripts/credentials.test.ts similarity index 72% rename from examples/integration-scripts/credentials.ts rename to examples/integration-scripts/credentials.test.ts index 39c7f632..64c03c44 100644 --- a/examples/integration-scripts/credentials.ts +++ b/examples/integration-scripts/credentials.test.ts @@ -6,18 +6,30 @@ const boot_url = 'http://127.0.0.1:3903'; const WAN_WITNESS_AID = 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha'; const WIL_WITNESS_AID = 'BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM'; const WES_WITNESS_AID = 'BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX'; +const WITNESS_HOST = 'witness-demo'; +const WITNESS_OOBIS = [ + `http://${WITNESS_HOST}:5642/oobi/${WAN_WITNESS_AID}/controller?name=Wan&tag=witness`, + `http://${WITNESS_HOST}:5643/oobi/${WIL_WITNESS_AID}/controller?name=Wil&tag=witness`, + `http://${WITNESS_HOST}:5644/oobi/${WES_WITNESS_AID}/controller?name=Wes&tag=witness`, +]; + const KLI_WITNESS_DEMO_PREFIXES = [ WAN_WITNESS_AID, WIL_WITNESS_AID, WES_WITNESS_AID, ]; +// Credential Schema discovery through credential schema OOBI resolution +const qviSchemaSAID = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao'; +const vLEIServerHostUrl = 'http://vlei-server:7723/oobi'; +const schemaOobiUrl = `${vLEIServerHostUrl}/${qviSchemaSAID}`; + // Boots an agent and connects to it, returning the connected SignifyClient async function bootAndConnect( - bran: string = signify.randomPasscode(), - agentUrl: string = url, - bootUrl: string = boot_url, - tier: signify.Tier = signify.Tier.low + bran: string = signify.randomPasscode(), + agentUrl: string = url, + bootUrl: string = boot_url, + tier: signify.Tier = signify.Tier.low ) { const client = new signify.SignifyClient(agentUrl, bran, tier, bootUrl); await client.boot(); @@ -40,8 +52,8 @@ interface Notification { // checks for notifications on a route and returns them as an array async function waitForNotifications( - client: signify.SignifyClient, - route: string + client: signify.SignifyClient, + route: string ): Promise { const awaitedNotifications = []; while (true) { @@ -58,36 +70,44 @@ async function waitForNotifications( return awaitedNotifications; } -await run(); +async function resolveWitnesses(client: signify.SignifyClient) { + await Promise.all( + WITNESS_OOBIS.map((oobi) => client.oobis().resolve(oobi)) + ); +} -// main test function -async function run() { +test('credentials', async () => { await signify.ready(); // Boot three clients one each for issuer, holder, and verifier const issuerClient = await bootAndConnect(signify.randomPasscode()); const holderClient = await bootAndConnect(signify.randomPasscode()); const verifierClient = await bootAndConnect(signify.randomPasscode()); + await Promise.all([ + resolveWitnesses(issuerClient), + resolveWitnesses(holderClient), + resolveWitnesses(verifierClient), + ]); const state1 = await issuerClient.state(); const state2 = await holderClient.state(); const state3 = await verifierClient.state(); console.log( - 'Issuer connected.\n\tHolder Controller AID:', - state1.controller.state.i, - '\n\tIssuer Agent AID: ', - state1.agent.i + 'Issuer connected.\n\tHolder Controller AID:', + state1.controller.state.i, + '\n\tIssuer Agent AID: ', + state1.agent.i ); console.log( - 'Holder connected.\n\tHolder Controller AID:', - state2.controller.state.i, - '\n\tHolder Agent AID: ', - state2.agent.i + 'Holder connected.\n\tHolder Controller AID:', + state2.controller.state.i, + '\n\tHolder Agent AID: ', + state2.agent.i ); console.log( - 'Verifier connected.\n\tVerifier Controller AID:', - state3.controller.state.i, - '\n\tVerifier Agent AID: ', - state3.agent.i + 'Verifier connected.\n\tVerifier Controller AID:', + state3.controller.state.i, + '\n\tVerifier Agent AID: ', + state3.agent.i ); const issuerAidName = 'issuer'; @@ -105,7 +125,7 @@ async function run() { await new Promise((resolve) => setTimeout(resolve, 250)); } const issAidResp = await issuerClient.identifiers().get(issuerAidName); - const issuerAID = issAidResp.prefix + const issuerAID = issAidResp.prefix; await issuerClient .identifiers() .addEndRole(issuerAidName, 'agent', issuerClient!.agent!.pre); @@ -121,23 +141,25 @@ async function run() { await new Promise((resolve) => setTimeout(resolve, 250)); } const hldAidResp = await holderClient.identifiers().get(holderAidName); - const holderAID = hldAidResp.prefix + const holderAID = hldAidResp.prefix; await holderClient .identifiers() .addEndRole(holderAidName, 'agent', holderClient!.agent!.pre); console.log("Recipient's AID:", holderAID); - let verifierIcpRes = await verifierClient.identifiers().create(verifierAidName, { - toad: 3, - wits: [...KLI_WITNESS_DEMO_PREFIXES], - }); + let verifierIcpRes = await verifierClient + .identifiers() + .create(verifierAidName, { + toad: 3, + wits: [...KLI_WITNESS_DEMO_PREFIXES], + }); let vfyOp = await verifierIcpRes.op(); while (!vfyOp['done']) { vfyOp = await verifierClient.operations().get(vfyOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); } const vfyAidResp = await verifierClient.identifiers().get(verifierAidName); - const verifierAID = vfyAidResp.prefix + const verifierAID = vfyAidResp.prefix; await verifierClient .identifiers() .addEndRole(verifierAidName, 'agent', verifierClient!.agent!.pre); @@ -148,11 +170,6 @@ async function run() { // OOBIs for credential schema and agent discovery console.log('Resolving Schema and Agent OOBIs...'); - // Credential Schema discovery through credential schema OOBI resolution - const qviSchemaSAID = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao'; - const vLEIServerHostUrl = 'http://127.0.0.1:7723/oobi'; - let schemaOobiUrl = `${vLEIServerHostUrl}/${qviSchemaSAID}`; - console.log(`Resolving schema OOBIs with ${schemaOobiUrl}`); issOp = await issuerClient.oobis().resolve(schemaOobiUrl, 'schema'); while (!issOp['done']) { @@ -181,15 +198,21 @@ async function run() { console.log('Getting Agent OOBIs for issuer, holder, and verifier'); let issAgentOOBI = await issuerClient.oobis().get(issuerAidName, 'agent'); let hldAgentOOBI = await holderClient.oobis().get(holderAidName, 'agent'); - let vfyAgentOOBI = await verifierClient.oobis().get(verifierAidName, 'agent'); + let vfyAgentOOBI = await verifierClient + .oobis() + .get(verifierAidName, 'agent'); // issuer -> holder, verifier - issOp = await issuerClient.oobis().resolve(hldAgentOOBI.oobis[0], holderAidName); + issOp = await issuerClient + .oobis() + .resolve(hldAgentOOBI.oobis[0], holderAidName); while (!issOp['done']) { issOp = await issuerClient.operations().get(issOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); } - issOp = await issuerClient.oobis().resolve(vfyAgentOOBI.oobis[0], verifierAidName); + issOp = await issuerClient + .oobis() + .resolve(vfyAgentOOBI.oobis[0], verifierAidName); while (!issOp['done']) { issOp = await issuerClient.operations().get(issOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); @@ -197,12 +220,16 @@ async function run() { console.log('Issuer resolved 2 OOBIs: [holder, verifier]'); // holder -> issuer, verifier - hldOp = await holderClient.oobis().resolve(issAgentOOBI.oobis[0], issuerAidName); + hldOp = await holderClient + .oobis() + .resolve(issAgentOOBI.oobis[0], issuerAidName); while (!hldOp['done']) { hldOp = await holderClient.operations().get(hldOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); } - hldOp = await holderClient.oobis().resolve(vfyAgentOOBI.oobis[0], verifierAidName); + hldOp = await holderClient + .oobis() + .resolve(vfyAgentOOBI.oobis[0], verifierAidName); while (!hldOp['done']) { hldOp = await holderClient.operations().get(hldOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); @@ -210,12 +237,16 @@ async function run() { console.log('Holder resolved 2 OOBIs: [issuer, verifier]'); // verifier -> issuer, holder - vfyOp = await verifierClient.oobis().resolve(issAgentOOBI.oobis[0], issuerAidName); + vfyOp = await verifierClient + .oobis() + .resolve(issAgentOOBI.oobis[0], issuerAidName); while (!vfyOp['done']) { vfyOp = await verifierClient.operations().get(vfyOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); } - vfyOp = await verifierClient.oobis().resolve(hldAgentOOBI.oobis[0], holderAidName); + vfyOp = await verifierClient + .oobis() + .resolve(hldAgentOOBI.oobis[0], holderAidName); while (!vfyOp['done']) { vfyOp = await verifierClient.operations().get(vfyOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); @@ -247,10 +278,14 @@ async function run() { const vcdata = { LEI: '5493001KJTIIGC8Y1R17', }; - const issResult = await issuerClient - .credentials() - .issue(issuerAidName, registry.regk, schemaSAID, holderAID, vcdata); - issOp = await issResult.op(); + const issResult = await issuerClient.credentials().issue({ + issuerName: issuerAidName, + registryId: registry.regk, + schemaId: schemaSAID, + recipient: holderAID, + data: vcdata, + }); + issOp = issResult.op; while (!issOp['done']) { issOp = await issuerClient.operations().get(issOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); @@ -260,36 +295,20 @@ async function run() { assert.equal(issCreds[0].sad.s, schemaSAID); assert.equal(issCreds[0].sad.i, issuerAID); assert.equal(issCreds[0].status.s, '0'); // 0 = issued - console.log(`Issuer: credential created with data: ${JSON.stringify(vcdata)}`); - - // prepare IPEX GRANT message - const acdc = new signify.Serder(issResult.acdc); - const iss = issResult.iserder; - const ianc = issResult.anc; - - const sigers = issResult.sigs.map( - (sig: string) => new signify.Siger({ qb64: sig }) + console.log( + `Issuer: credential created with data: ${JSON.stringify(vcdata)}` ); - const ims = signify.d(signify.messagize(ianc, sigers)); - const atc = ims.substring(ianc.size); // attachment - let dt = createTimestamp(); // grant datetime - - const [grant, gsigs, gend] = await issuerClient - .ipex() - .grant( - issuerAidName, - holderAID, - '', - acdc, - issResult.acdcSaider, - iss, - issResult.issExnSaider, - issResult.anc, - atc, - undefined, - dt - ); + // prepare IPEX GRANT message + const dt = createTimestamp(); // grant datetime + const [grant, gsigs, gend] = await issuerClient.ipex().grant({ + senderName: issuerAidName, + acdc: issResult.acdc, + anc: issResult.anc, + iss: issResult.iss, + recipient: holderAID, + datetime: dt, + }); await issuerClient .exchanges() .sendFromEvents(issuerAidName, 'credential', grant, gsigs, gend, [ @@ -300,22 +319,20 @@ async function run() { // from the holder's perspective - wait for GRANT notification, // perform admit, then mark GRANT notification as read const holderNotifications = await waitForNotifications( - holderClient, - '/exn/ipex/grant' + holderClient, + '/exn/ipex/grant' ); const grantNotification = holderNotifications[0]; // should only have one notification right now // Note: Credentials are no longer automatically accepted into a wallet. // Pending an implementation in KERIA there will be the ability to // auto-add credentials by automatically admitting credentials. - const [admit, sigs, aend] = await holderClient.ipex() - .admit( - holderAidName, - '', - grantNotification.a.d!, - createTimestamp()); - await holderClient.ipex() - .submitAdmit(holderAidName, admit, sigs, aend, [issuerAID]); + const [admit, sigs, aend] = await holderClient + .ipex() + .admit(holderAidName, '', grantNotification.a.d!, createTimestamp()); + await holderClient + .ipex() + .submitAdmit(holderAidName, admit, sigs, aend, [issuerAID]); console.log('Holder: IPEX ADMIT sent'); await holderClient.notifications().mark(grantNotification.i); @@ -329,28 +346,21 @@ async function run() { holderCreds = await holderClient.credentials().list(); } const hldVleiAcdc: any = holderCreds[0]; - assert.equal(holderCreds.length, 1) - assert.equal(hldVleiAcdc.sad.s, schemaSAID) - assert.equal(hldVleiAcdc.sad.i, issuerAID) - assert.equal(hldVleiAcdc.status.s, "0") // 0 = issued + assert.equal(holderCreds.length, 1); + assert.equal(hldVleiAcdc.sad.s, schemaSAID); + assert.equal(hldVleiAcdc.sad.i, issuerAID); + assert.equal(hldVleiAcdc.status.s, '0'); // 0 = issued console.log('Credential received by recipient'); // Present credential - const [grant2, gsigs2, gend2] = await holderClient - .ipex() - .grant( - holderAidName, - verifierAID, - '', - acdc, - issResult.acdcSaider, - iss, - issResult.issExnSaider, - issResult.anc, - atc, - undefined, - createTimestamp() - ); + const [grant2, gsigs2, gend2] = await holderClient.ipex().grant({ + senderName: holderAidName, + recipient: verifierAID, + acdc: issResult.acdc, + anc: issResult.anc, + iss: issResult.iss, + datetime: createTimestamp(), + }); await holderClient .exchanges() .sendFromEvents(holderAidName, 'presentation', grant2, gsigs2, gend2, [ @@ -360,15 +370,17 @@ async function run() { // Verifier check issued credential const verifierNotifications = await waitForNotifications( - verifierClient, - '/exn/ipex/grant' + verifierClient, + '/exn/ipex/grant' ); let verifierGrantNote = verifierNotifications[0]; - const [admit3, sigs3, aend3] = await verifierClient.ipex() - .admit(verifierAidName, '', verifierGrantNote.a.d!, createTimestamp()); - await verifierClient.ipex() - .submitAdmit(verifierAidName, admit3, sigs3, aend3, [holderAID]); + const [admit3, sigs3, aend3] = await verifierClient + .ipex() + .admit(verifierAidName, '', verifierGrantNote.a.d!, createTimestamp()); + await verifierClient + .ipex() + .submitAdmit(verifierAidName, admit3, sigs3, aend3, [holderAID]); console.log('Verifier: Admit sent for presentation'); await verifierClient.notifications().mark(verifierGrantNote.i); @@ -381,20 +393,22 @@ async function run() { await new Promise((resolve) => setTimeout(resolve, 250)); verifierCreds = await verifierClient.credentials().list(); } - assert.equal(verifierCreds.length, 1) - assert.equal(verifierCreds[0].sad.s, schemaSAID) - assert.equal(verifierCreds[0].sad.i, issuerAID) - assert.equal(verifierCreds[0].status.s, "0") // 0 = issued + assert.equal(verifierCreds.length, 1); + assert.equal(verifierCreds[0].sad.s, schemaSAID); + assert.equal(verifierCreds[0].sad.i, issuerAID); + assert.equal(verifierCreds[0].status.s, '0'); // 0 = issued console.log('Credential presented and received by verifier'); // Revoke credential - issOp = await issuerClient.credentials().revoke(issuerAidName, issCreds[0].sad.d); + issOp = await issuerClient + .credentials() + .revoke(issuerAidName, issCreds[0].sad.d); while (!issOp['done']) { issOp = await issuerClient.operations().get(issOp.name); await new Promise((resolve) => setTimeout(resolve, 250)); } issCreds = await issuerClient.credentials().list(); - const issVleiAcdc = issCreds[0] + const issVleiAcdc = issCreds[0]; assert.equal(issCreds.length, 1); assert.equal(issVleiAcdc.sad.s, schemaSAID); assert.equal(issVleiAcdc.sad.i, issuerAID); @@ -427,4 +441,4 @@ async function run() { // assert.equal(creds3[0].sad.i, aid1.prefix); // assert.equal(creds3[0].status.s, '1'); // 1 = revoked // console.log('Revocation presented and received by verifier'); -} +}, 60000); diff --git a/examples/integration-scripts/multisig.test.ts b/examples/integration-scripts/multisig.test.ts index a0af2b91..12b94632 100644 --- a/examples/integration-scripts/multisig.test.ts +++ b/examples/integration-scripts/multisig.test.ts @@ -1,18 +1,21 @@ import { strict as assert } from 'assert'; -import signify, { SignifyClient, Serder } from 'signify-ts'; - -const URL = 'http://127.0.0.1:3901'; -const BOOT_URL = 'http://127.0.0.1:3903'; -const WITNESS_HOST = process.env.WITHESS_HOST ?? "witness-demo"; -const WITNESS_OOBIS = [ - `http://${WITNESS_HOST}:5642/oobi`, - `http://${WITNESS_HOST}:5643/oobi`, - `http://${WITNESS_HOST}:5644/oobi`, +import signify, { + SignifyClient, + Serder, + IssueCredentialResult, +} from 'signify-ts'; +import { resolveEnvironment } from './utils/resolve-env'; + +const { url, bootUrl, witnessUrls, vleiServerUrl } = resolveEnvironment(); +const WITNESS_AIDS = [ + 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha', + 'BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM', + 'BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX', ]; -const SCHEMA_HOST = process.env.SCHEMA_HOST ?? 'vlei-server'; +const WITNESS_OOBIS = witnessUrls.map((url) => `${url}/oobi`); const SCHEMA_SAID = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao'; -const SCHEMA_OOBI = `http://${SCHEMA_HOST}:7723/oobi/${SCHEMA_SAID}`; +const SCHEMA_OOBI = `${vleiServerUrl}/oobi/${SCHEMA_SAID}`; test('multisig', async function run() { await signify.ready(); @@ -26,10 +29,10 @@ test('multisig', async function run() { // Create four identifiers, one for each client let [aid1, aid2, aid3, aid4] = await Promise.all([ - createAID(client1, 'member1', WITNESS_OOBIS), - createAID(client2, 'member2', WITNESS_OOBIS), - createAID(client3, 'member3', WITNESS_OOBIS), - createAID(client4, 'holder', WITNESS_OOBIS), + createAID(client1, 'member1', WITNESS_AIDS), + createAID(client2, 'member2', WITNESS_AIDS), + createAID(client3, 'member3', WITNESS_AIDS), + createAID(client4, 'holder', WITNESS_AIDS), ]); // Exchange OOBIs @@ -875,48 +878,16 @@ test('multisig', async function run() { let holder = aid4.prefix; let TIME = new Date().toISOString().replace('Z', '000+00:00'); - let credRes = await client1 - .credentials() - .issue( - 'multisig', - regk, - SCHEMA_SAID, - holder, - vcdata, - undefined, - undefined, - TIME - ); - op1 = await credRes.op(); - - let acdc = new signify.Serder(credRes.acdc); - let iss = credRes.iserder; - let ianc = credRes.anc; - let isigs = credRes.sigs; - let acdcSaider = credRes.acdcSaider; - let issExnSaider = credRes.issExnSaider; - - sigers = isigs.map((sig: any) => new signify.Siger({ qb64: sig })); - ims = signify.d(signify.messagize(ianc, sigers)); - let atc1 = ims.substring(ianc.size); - - let vcembeds = { - acdc: [acdc, ''], - iss: [iss, ''], - anc: [ianc, atc1], - }; - recp = [aid2['state'], aid3['state']].map((state) => state['i']); - await client1 - .exchanges() - .send( - 'member1', - 'multisig', - aid1, - '/multisig/iss', - { gid: aid }, - vcembeds, - recp - ); + let credRes = await client1.credentials().issue({ + issuerName: 'multisig', + registryId: regk, + schemaId: SCHEMA_SAID, + data: vcdata, + recipient: holder, + datetime: TIME, + }); + op1 = credRes.op; + await multisigIssue(client1, 'member1', 'multisig', credRes); console.log( 'Member1 initiated credential creation, waiting for others to join...' @@ -930,48 +901,17 @@ test('multisig', async function run() { res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; - let credRes2 = await client2 - .credentials() - .issue( - 'multisig', - regk2, - SCHEMA_SAID, - holder, - vcdata, - undefined, - undefined, - exn.e.acdc.a.dt - ); - - op2 = await credRes2.op(); - - acdc = new signify.Serder(credRes2.acdc); - iss = credRes2.iserder; - ianc = credRes2.anc; - isigs = credRes2.sigs; - - sigers = isigs.map((sig: any) => new signify.Siger({ qb64: sig })); - ims = signify.d(signify.messagize(ianc, sigers)); - let atc2 = ims.substring(ianc.size); - - vcembeds = { - acdc: [acdc, ''], - iss: [iss, ''], - anc: [ianc, atc2], - }; + const credRes2 = await client2.credentials().issue({ + issuerName: 'multisig', + registryId: regk2, + schemaId: SCHEMA_SAID, + data: vcdata, + datetime: exn.e.acdc.a.dt, + recipient: holder, + }); - recp = [aid1['state'], aid3['state']].map((state) => state['i']); - await client2 - .exchanges() - .send( - 'member2', - 'multisig', - aid2, - '/multisig/iss', - { gid: aid }, - vcembeds, - recp - ); + op2 = credRes2.op; + await multisigIssue(client2, 'member2', 'multisig', credRes2); console.log('Member2 joins credential create event, waiting for others...'); // Member3 check for notifications and join the create registry event @@ -982,47 +922,17 @@ test('multisig', async function run() { res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - let credRes3 = await client3 - .credentials() - .issue( - 'multisig', - regk3, - SCHEMA_SAID, - holder, - vcdata, - undefined, - undefined, - exn.e.acdc.a.dt - ); - - op3 = await credRes3.op(); - acdc = new signify.Serder(credRes3.acdc); - iss = credRes3.iserder; - ianc = credRes3.anc; - isigs = credRes3.sigs; - - sigers = isigs.map((sig: any) => new signify.Siger({ qb64: sig })); - ims = signify.d(signify.messagize(ianc, sigers)); - let atc3 = ims.substring(ianc.size); - - vcembeds = { - acdc: [acdc, ''], - iss: [iss, ''], - anc: [ianc, atc3], - }; + let credRes3 = await client3.credentials().issue({ + issuerName: 'multisig', + registryId: regk3, + schemaId: SCHEMA_SAID, + recipient: holder, + data: vcdata, + datetime: exn.e.acdc.a.dt, + }); - recp = [aid1['state'], aid2['state']].map((state) => state['i']); - await client3 - .exchanges() - .send( - 'member3', - 'multisig', - aid3, - '/multisig/iss', - { gid: aid }, - vcembeds, - recp - ); + op3 = credRes3.op; + await multisigIssue(client3, 'member3', 'multisig', credRes3); console.log('Member3 joins credential create event, waiting for others...'); // Check completion @@ -1047,21 +957,14 @@ test('multisig', async function run() { console.log('Starting grant message'); stamp = new Date().toISOString().replace('Z', '000+00:00'); - let [grant, gsigs, end] = await client1 - .ipex() - .grant( - 'multisig', - holder, - '', - acdc, - acdcSaider, - iss, - issExnSaider, - ianc, - atc1, - undefined, - stamp - ); + let [grant, gsigs, end] = await client1.ipex().grant({ + senderName: 'multisig', + acdc: credRes.acdc, + anc: credRes.anc, + iss: credRes.iss, + recipient: holder, + datetime: stamp, + }); await client1 .exchanges() @@ -1102,21 +1005,14 @@ test('multisig', async function run() { res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; - let [grant2, gsigs2, end2] = await client2 - .ipex() - .grant( - 'multisig', - holder, - '', - acdc, - acdcSaider, - iss, - issExnSaider, - ianc, - atc2, - undefined, - stamp - ); + let [grant2, gsigs2, end2] = await client2.ipex().grant({ + senderName: 'multisig', + recipient: holder, + acdc: credRes2.acdc, + anc: credRes2.anc, + iss: credRes3.iss, + datetime: stamp, + }); await client2 .exchanges() @@ -1153,21 +1049,14 @@ test('multisig', async function run() { res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - let [grant3, gsigs3, end3] = await client3 - .ipex() - .grant( - 'multisig', - holder, - '', - acdc, - acdcSaider, - iss, - issExnSaider, - ianc, - atc3, - undefined, - stamp - ); + let [grant3, gsigs3, end3] = await client3.ipex().grant({ + senderName: 'multisig', + recipient: holder, + acdc: credRes3.acdc, + anc: credRes3.anc, + iss: credRes3.iss, + datetime: stamp, + }); await client3 .exchanges() @@ -1244,7 +1133,7 @@ async function waitForMessage(client: SignifyClient, route: string) { async function bootClient(): Promise { let bran = signify.randomPasscode(); - let client = new SignifyClient(URL, bran, signify.Tier.low, BOOT_URL); + let client = new SignifyClient(url, bran, signify.Tier.low, bootUrl); await client.boot(); await client.connect(); let state = await client.state(); @@ -1254,15 +1143,12 @@ async function bootClient(): Promise { 'Agent AID: ', state.agent.i ); + + await resolveWitnesses(client); return client; } -async function createAID( - client: SignifyClient, - name: string, - witnesses: string[] -) { - const wits = await resolveWitnessAids(client, witnesses); +async function createAID(client: SignifyClient, name: string, wits: string[]) { let icpResult1 = await client.identifiers().create(name, { toad: wits.length, wits: wits, @@ -1275,14 +1161,47 @@ async function createAID( return aid; } -async function resolveWitnessAids(client: SignifyClient, oobis: string[]) { - return await Promise.all( - oobis.map(async (oobi) => { - const op = await waitForOp( - client, - await client.oobis().resolve(oobi) - ); - return op.response.i; - }) +async function resolveWitnesses(client: SignifyClient) { + await Promise.all( + WITNESS_OOBIS.map((oobi) => client.oobis().resolve(oobi)) ); } + +async function multisigIssue( + client: SignifyClient, + memberName: string, + groupName: string, + result: IssueCredentialResult +) { + 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(result.anc.raw)); + const sigers = sigs.map((sig: string) => new signify.Siger({ qb64: sig })); + const ims = signify.d(signify.messagize(result.anc, sigers)); + const atc = ims.substring(result.anc.size); + + const embeds = { + acdc: [result.acdc, ''], + iss: [result.iss, ''], + anc: [result.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/iss', + { gid: groupHab.prefix }, + embeds, + recipients + ); +} diff --git a/examples/integration-scripts/single-issuer-holder.test.ts b/examples/integration-scripts/single-issuer-holder.test.ts index 9bac4fe3..fa6e838f 100644 --- a/examples/integration-scripts/single-issuer-holder.test.ts +++ b/examples/integration-scripts/single-issuer-holder.test.ts @@ -1,10 +1,8 @@ import assert from 'node:assert'; import signify, { SignifyClient, - CredentialResult, - messagize, - d, - Siger, + IssueCredentialArgs, + Operation, } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; @@ -40,7 +38,7 @@ async function createIdentifier( wits: witnesses, }); const op = await icpResult1.op(); - await waitOperation(client, op.name, 5000); + await waitOperation(client, op, 5000); const aid = await client.identifiers().get(name); if (!client.agent) { @@ -63,7 +61,7 @@ async function getAgentOobi( async function resolveOobi(client: SignifyClient, oobi: string, alias: string) { console.log(`Resolve ${alias} -> ${oobi}`); const op = await client.oobis().resolve(oobi, alias); - const result = await waitOperation(client, op.name, 5000); + const result = await waitOperation<{ i: string }>(client, op, 5000); return result.response; } @@ -74,7 +72,7 @@ async function createRegistry( ) { const result = await client.registries().create({ name, registryName }); const op = await result.op(); - await waitOperation(client, op.name, 5000); + await waitOperation(client, op, 5000); const registries = await client.registries().list(name); assert.equal(registries.length, 1); @@ -85,51 +83,35 @@ async function createRegistry( async function issueCredential( client: SignifyClient, - name: string, - args: { registry: string; schema: string; recipient: string; data: unknown } + args: IssueCredentialArgs ) { - const result: CredentialResult = await client - .credentials() - .issue(name, args.registry, args.schema, args.recipient, args.data); + const result = await client.credentials().issue(args); - const op = await result.op(); - await waitOperation(client, op.name, 5000); + await waitOperation(client, result.op, 5000); const creds = await client.credentials().list(); assert.equal(creds.length, 1); - assert.equal(creds[0].sad.s, args.schema); + assert.equal(creds[0].sad.s, args.schemaId); assert.equal(creds[0].status.s, '0'); - const acdc = new signify.Serder(result.acdc); - const iss = result.iserder; - const ianc = result.anc; - - const sigers = result.sigs.map((sig: string) => new Siger({ qb64: sig })); - const ims = d(messagize(ianc, sigers)); - - const atc = ims.substring(result.anc.size); const dt = createTimestamp(); - const [grant, gsigs, end] = await client - .ipex() - .grant( - name, - args.recipient, - '', - acdc, - result.acdcSaider, - iss, - result.issExnSaider, - result.anc, - atc, - undefined, - dt - ); - await client - .exchanges() - .sendFromEvents(name, 'credential', grant, gsigs, end, [ - args.recipient, - ]); + if (args.recipient) { + const [grant, gsigs, end] = await client.ipex().grant({ + senderName: args.issuerName, + recipient: args.recipient, + datetime: dt, + acdc: result.acdc, + anc: result.anc, + iss: result.iss, + }); + + await client + .exchanges() + .sendFromEvents(args.issuerName, 'credential', grant, gsigs, end, [ + args.recipient, + ]); + } console.log('Grant message sent'); @@ -189,29 +171,25 @@ async function wait(fn: () => Promise, timeout: number = 10000) { throw new RetryError(`Retry failed after ${Date.now() - start} ms`, errors); } -async function waitOperation( +async function waitOperation( client: SignifyClient, - name: string, - timeout?: number -): Promise { - const now = Date.now(); - let op = await client.operations().get(name); - - while (!op['done']) { - op = await client.operations().get(name); - if (op['done']) { - return op; - } + op: Operation, + timeout: number = 30000 +): Promise> { + const start = Date.now(); + while (Date.now() - start < timeout) { + const current = (await client + .operations() + .get(op.name)) as Operation; - const elapsed = Date.now() - now; - if (timeout !== undefined && elapsed > timeout) { - throw new Error( - `Operation '${op.name}' time out after ${elapsed} ms` - ); + if (current.done) { + return current; } await new Promise((resolve) => setTimeout(resolve, 1000)); } + + throw new Error(`Operation timed out after ${Date.now() - start}ms`); } class RetryError extends Error { @@ -286,9 +264,10 @@ test( await createRegistry(issuerClient, 'issuer', 'vLEI'); const registires = await issuerClient.registries().list('issuer'); - await issueCredential(issuerClient, 'issuer', { - registry: registires[0].regk, - schema: SCHEMA_SAID, + await issueCredential(issuerClient, { + issuerName: 'issuer', + registryId: registires[0].regk, + schemaId: SCHEMA_SAID, recipient: holderPrefix, data: { LEI: '5493001KJTIIGC8Y1R17', diff --git a/src/keri/app/coring.ts b/src/keri/app/coring.ts index de96b104..193f7588 100644 --- a/src/keri/app/coring.ts +++ b/src/keri/app/coring.ts @@ -60,6 +60,12 @@ export class Oobis { } } +export interface Operation { + done: boolean; + name: string; + response: T; +} + /** * Operations * @remarks @@ -79,9 +85,9 @@ export class Operations { * Get operation status * @async * @param {string} name Name of the operation - * @returns {Promise} A promise to the status of the operation + * @returns {Promise} A promise to the status of the operation */ - async get(name: string): Promise { + async get(name: string): Promise> { let path = `/operations/${name}`; let data = null; let method = 'GET'; diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 3e7074d3..6676a6d6 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -21,6 +21,7 @@ import { serializeACDCAttachment, serializeIssExnAttachment, } from '../core/utils'; +import { Operation } from './coring'; /** Types of credentials */ export class CredentialTypes { @@ -36,61 +37,84 @@ export interface CredentialFilter { limit?: number; } -export class CredentialResult { - private readonly _acdc: any; - private readonly _iserder: Serder; - private readonly _anc: Serder; - private readonly _sigs: string[]; - private readonly _acdcSaider: Saider; - private readonly _issExnSaider: Saider; - private readonly promise: Promise; +export interface IssueCredentialArgs { + /** + * Name of the issuer identifier + */ + issuerName: string; - constructor( - acdc: Dict, - iserder: Serder, - anc: Serder, - sigs: any[], - acdcSaider: Saider, - issExnSaider: Saider, - promise: Promise - ) { - this._acdc = acdc; - this._iserder = iserder; - this._anc = anc; - this._sigs = sigs; - this._acdcSaider = acdcSaider; - this._issExnSaider = issExnSaider; - this.promise = promise; - } + /** + * QB64 AID of credential registry + */ + registryId: string; - get acdc() { - return this._acdc; - } + /** + * SAID Of the schema + */ + schemaId: string; - get iserder() { - return this._iserder; - } + /** + * Prefix of recipient identifier + */ + recipient?: string; - get acdcSaider() { - return this._acdcSaider; - } + /** + * Credential data + */ + data?: Record; - get issExnSaider() { - return this._issExnSaider; - } + /** + * Credential rules + */ + rules?: Record; - get anc() { - return this._anc; - } + /** + * Credential sources + */ + source?: Record; - get sigs() { - return this._sigs; - } + /** + * Datetime to set for the credential + */ + datetime?: string; - async op(): Promise { - let res = await this.promise; - return await res.json(); - } + /** + * Flag to issue a credential with privacy preserving features + */ + privacy?: boolean; +} + +export interface IssueCredentialResult { + acdc: Serder; + anc: Serder; + iss: Serder; + op: Operation; +} + +export interface IpexGrantArgs { + /** + * Alias for the IPEX sender AID + */ + senderName: string; + + /** + * Prefix of the IPEX recipient AID + */ + recipient: string; + + /** + * Message to send + */ + message?: string; + + /** + * qb64 SAID of agree message this grant is responding to + */ + agree?: string; + datetime?: string; + acdc: Serder; + iss: Serder; + anc: Serder; } /** @@ -156,161 +180,94 @@ export class Credentials { /** * Issue a credential - * @async - * @param {string} name Name or alias of the identifier - * @param {string} registy qb64 AID of credential registry - * @param {string} schema SAID of the schema - * @param {string} [recipient] Optional prefix of recipient identifier - * @param {any} [credentialData] Optional credential data - * @param {any} [rules] Optional credential rules - * @param {any} [source] Optional credential sources - * @param {string} [datetime] Optional datetime to set for the credential - * @param {boolean} [priv=false] Flag to issue a credential with privacy preserving features - * @returns {Promise} A promise to the long-running operation */ - async issue( - name: string, - registy: string, - schema: string, - recipient?: string, - credentialData?: any, - rules?: any, - source?: any, - datetime?: string, - priv: boolean = false - ): Promise { - // Create Credential - let hab = await this.client.identifiers().get(name); - let pre: string = hab.prefix; - - const dt = - datetime === undefined - ? new Date().toISOString().replace('Z', '000+00:00') - : datetime; - - const vsacdc = versify(Ident.ACDC, undefined, Serials.JSON, 0); - const vs = versify(Ident.KERI, undefined, Serials.JSON, 0); - - let cred: any = { - v: vsacdc, - d: '', - }; - let subject: any = { - d: '', - }; - if (priv) { - cred.u = new Salter({}); - subject.u = new Salter({}); + async issue(args: IssueCredentialArgs): Promise { + const hab = await this.client.identifiers().get(args.issuerName); + const estOnly = hab.state.c !== undefined && hab.state.c.includes('EO'); + if (estOnly) { + // TODO implement rotation event + throw new Error('Establishment only not implemented'); } - if (recipient != undefined) { - subject.i = recipient; + if (!this.client.manager) { + throw new Error('No manager on client'); } - subject.dt = dt; - subject = { ...subject, ...credentialData }; - const [, a] = Saider.saidify(subject, undefined, undefined, 'd'); + const keeper = this.client.manager.get(hab); - cred = { ...cred, i: pre }; - cred.ri = registy; - cred = { ...cred, ...{ s: schema }, ...{ a: a } }; + const dt = + args.datetime ?? new Date().toISOString().replace('Z', '000+00:00'); - if (source !== undefined) { - cred.e = source; - } - if (rules !== undefined) { - cred.r = rules; - } - const [vcSaider, vc] = Saider.saidify(cred); + const [, subject] = Saider.saidify({ + d: '', + u: args.privacy ? new Salter({}) : undefined, + i: args.recipient, + dt: dt, + ...args.data, + }); - // Create iss - let _iss = { - v: vs, + const [, acdc] = Saider.saidify({ + v: versify(Ident.ACDC, undefined, Serials.JSON, 0), + d: '', + u: args.privacy ? new Salter({}) : undefined, + i: hab.prefix, + ri: args.registryId, + s: args.schemaId, + a: subject, + e: args.source, + r: args.rules, + }); + + const [, iss] = Saider.saidify({ + v: versify(Ident.KERI, undefined, Serials.JSON, 0), t: Ilks.iss, d: '', - i: vc.d, + i: acdc.d, s: '0', - ri: registy, + ri: args.registryId, dt: dt, - }; - - let [issSaider, iss] = Saider.saidify(_iss); - let iserder = new Serder(iss); - - // Create paths and sign - let keeper = this.client!.manager!.get(hab); - - let state = hab.state; - if (state.c !== undefined && state.c.includes('EO')) { - var estOnly = true; - } else { - var estOnly = false; - } - let sn = Number(state.s); - let dig = state.d; - - let data: any = [ - { - i: iss.i, - s: iss.s, - d: iss.d, - }, - ]; + }); - // Create ixn - let ixn = {}; - let anc: Serder; - let sigs = []; - if (estOnly) { - // TODO implement rotation event - throw new Error('Establishment only not implemented'); - } else { - anc = interact({ - pre: pre, - sn: sn + 1, - data: data, - dig: dig, - version: undefined, - kind: undefined, - }); - sigs = await keeper.sign(b(anc.raw)); - ixn = anc.ked; - } + const sn = Number(hab.state.s); + const anc = interact({ + pre: hab.prefix, + sn: sn + 1, + data: [ + { + i: iss.i, + s: iss.s, + d: iss.d, + }, + ], + dig: hab.state.d, + version: undefined, + kind: undefined, + }); - let res = this.issueFromEvents(hab, name, vc, iss, ixn, sigs); - return new CredentialResult( - vc, - iserder, - anc, - sigs, - vcSaider, - issSaider, - res - ); - } + const sigs = await keeper.sign(b(anc.raw)); - issueFromEvents( - hab: Dict, - name: string, - vc: Dict, - iss: Dict, - ixn: Dict, - sigs: any[] - ) { - let path = `/identifiers/${name}/credentials`; - let method = 'POST'; - let body: any = { - acdc: vc, + const path = `/identifiers/${hab.name}/credentials`; + const method = 'POST'; + const body = { + acdc: acdc, iss: iss, - ixn: ixn, - sigs: sigs, + ixn: anc.ked, + sigs, + [keeper.algo]: keeper.params(), }; - let keeper = this.client!.manager!.get(hab); - body[keeper.algo] = keeper.params(); - let headers = new Headers({ + const headers = new Headers({ Accept: 'application/json+cesr', }); - return this.client.fetch(path, method, body, headers); + + const res = await this.client.fetch(path, method, body, headers); + const op = await res.json(); + + return { + acdc: new Serder(acdc), + iss: new Serder(iss), + anc, + op, + }; } /** @@ -740,46 +697,24 @@ export class Ipex { /** * Create an IPEX grant EXN message - * @async - * @param {string} name Name or alias of the identifier - * @param {string} recp qb64 AID of recipient of the grant - * @param {string} message accompany human readable description of the credential being issued - * @param {Serder} acdc Credential - * @param acdcSaider The Saider instance of an ACDC. Typically comes from the Creder instance yet Creder is not yet ported to SignifyTS - * @param {Serder} iss TEL issuance event - * @param issSaider The Saider instance of the iss EXN interaction event for an ACDC. ypically comes from the Creder instance yet Creder is not yet ported to SignifyTS - * @param {Serder} anc Anchoring event - * @param {string} atc attachments for the anchoring event - * @param {string} agree Option qb64 SAID of agree message this grant is responding to - * @param {string} datetime Optional datetime to set for the credential - * @returns {Promise} A promise to the long-running operation */ - async grant( - name: string, - recp: string, - message: string, - acdc: Serder, - acdcSaider: Saider, - iss: Serder, - issSaider: Saider, - anc: Serder, - atc: string, - agree?: string, - datetime?: string - ): Promise<[Serder, string[], string]> { - let hab = await this.client.identifiers().get(name); - let data: any = { - m: message, - i: recp, + async grant(args: IpexGrantArgs): Promise<[Serder, string[], string]> { + const hab = await this.client.identifiers().get(args.senderName); + const data = { + m: args.message ?? '', + i: args.recipient, }; - let acdcAtc = d(serializeACDCAttachment(acdc, acdcSaider)); - let issAtc = d(serializeIssExnAttachment(anc, issSaider)); + const keeper = this.client.manager?.get(hab); + const sigs = await keeper.sign(b(args.anc.raw)); + const sigers = sigs.map((sig: string) => new Siger({ qb64: sig })); + const ims = d(messagize(args.anc, sigers)); + const atc = ims.substring(args.anc.size); - let embeds: any = { - acdc: [acdc, acdcAtc], - iss: [iss, issAtc], - anc: [anc, atc], + const embeds: Record = { + acdc: [args.acdc, d(serializeACDCAttachment(args.acdc))], + iss: [args.iss, d(serializeIssExnAttachment(args.anc))], + anc: [args.anc, atc], }; return this.client @@ -790,8 +725,8 @@ export class Ipex { data, embeds, undefined, - datetime, - agree + args.datetime, + args.agree ); } diff --git a/src/keri/core/utils.ts b/src/keri/core/utils.ts index 2db51abc..15e8f7c5 100644 --- a/src/keri/core/utils.ts +++ b/src/keri/core/utils.ts @@ -99,7 +99,7 @@ export function range(start: number, stop: number, step: number) { export function intToBytes(value: number, length: number): Uint8Array { const byteArray = new Uint8Array(length); // Assuming a 4-byte integer (32 bits) - for (let index = byteArray.length-1; index >= 0; index--) { + for (let index = byteArray.length - 1; index >= 0; index--) { let byte = value & 0xff; byteArray[index] = byte; value = (value - byte) / 256; @@ -109,18 +109,16 @@ export function intToBytes(value: number, length: number): Uint8Array { export function bytesToInt(ar: Uint8Array): number { let value = 0; - for (let i = 0; i { const registry = 'EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX'; const schema = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao'; const isuee = 'EG2XjQN-3jPN5rcR4spLjaJyM4zA6Lgg-Hd5vSMymu5p'; - await credentials.issue( - 'aid1', - registry, - schema, - isuee, - { LEI: '1234' }, - {}, - {}, - undefined, - false - ); + await credentials.issue({ + issuerName: 'aid1', + registryId: registry, + schemaId: schema, + recipient: isuee, + data: { LEI: '1234' }, + source: {}, + rules: {}, + privacy: false, + }); lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; lastBody = JSON.parse(lastCall[1]!.body!.toString()); assert.equal(lastCall[0]!, url + '/identifiers/aid1/credentials'); @@ -359,19 +359,15 @@ describe('Ipex', () => { kind: undefined, }); - let [grant, gsigs, end] = await ipex.grant( - 'multisig', - holder, - '', - new Serder(acdc), - acdcSaider, - iserder, - issSaider, + let [grant, gsigs, end] = await ipex.grant({ + senderName: 'multisig', + recipient: holder, + message: '', + acdc: new Serder(acdc), + iss: iserder, anc, - '-vtest', - undefined, - mockCredential.sad.a.dt - ); + datetime: mockCredential.sad.a.dt, + }); assert.deepStrictEqual(grant.ked, { v: 'KERI10JSON0004b1_', @@ -426,8 +422,8 @@ describe('Ipex', () => { end, '-LAg4AACA' + '-e-acdc-IABBHsidiI6IkFDREMxMEpTT04wMDAxOTdfIiwiZCI6IkVN0AAAAAAAAAAAAAAAAAAAAAAAEMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo-LAW5AACAA' + - '-e-iss-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAAAENf3IEYwYtFmlq5ZzoI-zFzeR7E3ZNRN2YH_0KAFbdJW-LAE5AACAA' + - '-e-anc-vtest' + '-e-iss-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAAAECVCyxNpB4PJkpLbWqI02WXs1wf7VUxPNY2W28SN2qqm-LAa5AACAA' + + '-e-anc-AABAADMtDfNihvCSXJNp1VronVojcPGo--0YZ4Kh6CAnowRnn4Or4FgZQqaqCEv6XVS413qfZoVp8j2uxTTPkItO7ED' ); let [admit, asigs, aend] = await ipex.admit( diff --git a/test/core/utils.test.ts b/test/core/utils.test.ts index 40b294c2..7ddc1579 100644 --- a/test/core/utils.test.ts +++ b/test/core/utils.test.ts @@ -6,12 +6,12 @@ import { describe(serializeIssExnAttachment, () => { it('serializes iss data', () => { - const [saider, data] = Saider.saidify({ + const [, data] = Saider.saidify({ d: '', v: versify(Ident.KERI, undefined, Serials.JSON, 0), }); - const result = serializeIssExnAttachment(new Serder(data), saider); + const result = serializeIssExnAttachment(new Serder(data)); expect(d(result)).toEqual( '-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAAAEKZPmzJqhx76bcC2ftPQgeRirmOd8ZBOtGVqHJrSm7F1' @@ -21,7 +21,7 @@ describe(serializeIssExnAttachment, () => { describe(serializeACDCAttachment, () => { it('serializes acdc data', () => { - const [saider, data] = Saider.saidify({ + const [, data] = Saider.saidify({ d: '', v: versify(Ident.ACDC, undefined, Serials.JSON, 0), a: { @@ -29,7 +29,7 @@ describe(serializeACDCAttachment, () => { }, }); - const result = serializeACDCAttachment(new Serder(data), saider); + const result = serializeACDCAttachment(new Serder(data)); expect(d(result)).toEqual( '-IABBHsiZCI6IkVORTZzbWw4X1NMZVIzdk9NajRJRExLX2Nn0AAAAAAAAAAAAAAAAAAAAAAAENE6sml8_SLeR3vOMj4IDLK_cgd-A-vtg0Jnu7ozdBjW'