From 00a30096e7dd6779fc3607f1db8109f06cc2fc69 Mon Sep 17 00:00:00 2001 From: Fergal Date: Thu, 21 Mar 2024 13:29:31 +0000 Subject: [PATCH 1/6] feat: single-sig IPEX apply, offer, agree * feat: IPEX apply, offer, admit msgs * test: apply, offer, agree integration tests * fix: correct typing * test: ipex unit tests * test: improve test and docs * test: ipex better split of unit tests * test: whitespace diff --- .../integration-scripts/credentials.test.ts | 104 ++++- src/keri/app/credentialing.ts | 212 ++++++++++ test/app/credentialing.test.ts | 361 +++++++++++++++++- 3 files changed, 674 insertions(+), 3 deletions(-) diff --git a/examples/integration-scripts/credentials.test.ts b/examples/integration-scripts/credentials.test.ts index 1babbc26..52f3743d 100644 --- a/examples/integration-scripts/credentials.test.ts +++ b/examples/integration-scripts/credentials.test.ts @@ -51,6 +51,10 @@ let holderAid: Aid; let verifierAid: Aid; let legalEntityAid: Aid; +let applySaid: string; +let offerSaid: string; +let agreeSaid: string; + beforeAll(async () => { [issuerClient, holderClient, verifierClient, legalEntityClient] = await getOrCreateClients(4); @@ -282,7 +286,99 @@ test('single signature credentials', async () => { assert(holderCredential.atc !== undefined); }); - await step('holder IPEX present', async () => { + await step('verifier IPEX apply', async () => { + const [apply, sigs, _] = await verifierClient.ipex().apply({ + senderName: verifierAid.name, + schema: QVI_SCHEMA_SAID, + attributes: { LEI: "5493001KJTIIGC8Y1R17" }, + recipient: holderAid.prefix, + datetime: createTimestamp(), + }); + + const op = await verifierClient.ipex().submitApply( + verifierAid.name, apply, sigs, [holderAid.prefix] + ); + await waitOperation(verifierClient, op); + }); + + await step('holder IPEX apply receive and offer', async () => { + const holderNotifications = await waitForNotifications( + holderClient, + '/exn/ipex/apply' + ); + + const holderApplyNote = holderNotifications[0]; + assert(holderApplyNote.a.d); + + const apply = await holderClient.exchanges().get(holderApplyNote.a.d); + applySaid = apply.exn.d; + + let filter: { [x: string]: any } = { '-s': apply.exn.a.s }; + for (const key in apply.exn.a.a) { + filter[`-a-${key}`] = apply.exn.a.a[key]; + } + + const matchingCreds = await holderClient.credentials().list({ filter }); + expect(matchingCreds).toHaveLength(1); + + await markAndRemoveNotification(holderClient, holderNotifications[0]); + + const [offer, sigs, end] = await holderClient.ipex().offer({ + senderName: holderAid.name, + recipient: verifierAid.prefix, + acdc: new Serder(matchingCreds[0].sad), + apply: applySaid, + datetime: createTimestamp(), + }); + + const op = await holderClient.ipex().submitOffer(holderAid.name, offer, sigs, end, [verifierAid.prefix]); + await waitOperation(holderClient, op); + }); + + await step('verifier receive offer and agree', async () => { + const verifierNotifications = await waitForNotifications( + verifierClient, + '/exn/ipex/offer' + ); + + const verifierOfferNote = verifierNotifications[0]; + assert(verifierOfferNote.a.d); + + const offer = await verifierClient.exchanges().get(verifierOfferNote.a.d); + offerSaid = offer.exn.d; + + expect(offer.exn.p).toBe(applySaid); + expect(offer.exn.e.acdc.a.LEI).toBe("5493001KJTIIGC8Y1R17"); + + await markAndRemoveNotification(verifierClient, verifierOfferNote); + + const [agree, sigs, _] = await verifierClient.ipex().agree({ + senderName: verifierAid.name, + recipient: holderAid.prefix, + offer: offerSaid, + datetime: createTimestamp(), + }); + + const op = await verifierClient.ipex().submitAgree(verifierAid.name, agree, sigs, [holderAid.prefix]); + await waitOperation(verifierClient, op); + }); + + await step('holder IPEX receive agree and grant/present', async () => { + const holderNotifications = await waitForNotifications( + holderClient, + '/exn/ipex/agree' + ); + + const holderAgreeNote = holderNotifications[0]; + assert(holderAgreeNote.a.d); + + const agree = await holderClient.exchanges().get(holderAgreeNote.a.d); + agreeSaid = agree.exn.d; + + expect(agree.exn.p).toBe(offerSaid); + + await markAndRemoveNotification(holderClient, holderAgreeNote); + const holderCredential = await holderClient .credentials() .get(qviCredentialId); @@ -296,6 +392,7 @@ test('single signature credentials', async () => { acdcAttachment: holderCredential.atc, ancAttachment: holderCredential.ancatc, issAttachment: holderCredential.issAtc, + agree: agreeSaid, datetime: createTimestamp(), }); @@ -314,6 +411,10 @@ test('single signature credentials', async () => { ); const verifierGrantNote = verifierNotifications[0]; + assert(verifierGrantNote.a.d); + + const grant = await holderClient.exchanges().get(verifierGrantNote.a.d); + expect(grant.exn.p).toBe(agreeSaid); const [admit3, sigs3, aend3] = await verifierClient .ipex() @@ -347,6 +448,7 @@ test('single signature credentials', async () => { holderClient, '/exn/ipex/admit' ); + await markAndRemoveNotification(holderClient, holderNotifications[0]); }); diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 28b11ab2..5c2c31b6 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -96,6 +96,85 @@ export interface RevokeCredentialResult { op: Operation; } +export interface IpexApplyArgs { + /** + * Alias for the IPEX sender AID + */ + senderName: string; + + /** + * Prefix of the IPEX recipient AID + */ + recipient: string; + + /** + * Message to send + */ + message?: string; + + /** + * SAID of schema to apply for + */ + schema: string; + + /** + * Optional attributes for selective disclosure + */ + attributes?: Record; + datetime?: string; +} + +export interface IpexOfferArgs { + /** + * Alias for the IPEX sender AID + */ + senderName: string; + + /** + * Prefix of the IPEX recipient AID + */ + recipient: string; + + /** + * Message to send + */ + message?: string; + + /** + * ACDC to offer + */ + acdc: Serder; + + /** + * Optional qb64 SAID of apply message this offer is responding to + */ + apply?: string; + datetime?: string; +} + +export interface IpexAgreeArgs { + /** + * Alias for the IPEX sender AID + */ + senderName: string; + + /** + * Prefix of the IPEX recipient AID + */ + recipient: string; + + /** + * Message to send + */ + message?: string; + + /** + * qb64 SAID of offer message this agree is responding to + */ + offer: string; + datetime?: string; +} + export interface IpexGrantArgs { /** * Alias for the IPEX sender AID @@ -713,6 +792,139 @@ export class Ipex { this.client = client; } + /** + * Create an IPEX apply EXN message + */ + async apply(args: IpexApplyArgs): Promise<[Serder, string[], string]> { + const hab = await this.client.identifiers().get(args.senderName); + const data = { + m: args.message ?? '', + s: args.schema, + a: args.attributes ?? {}, + i: args.recipient, + }; + + return this.client + .exchanges() + .createExchangeMessage( + hab, + '/ipex/apply', + data, + {}, + undefined, + args.datetime, + undefined, + ); + } + + async submitApply( + name: string, + exn: Serder, + sigs: string[], + recp: string[] + ): Promise { + const body = { + exn: exn.ked, + sigs, + rec: recp, + }; + + const response = await this.client.fetch( + `/identifiers/${name}/ipex/apply`, + 'POST', + body + ); + + return response.json(); + } + + /** + * Create an IPEX offer EXN message + */ + async offer(args: IpexOfferArgs): Promise<[Serder, string[], string]> { + const hab = await this.client.identifiers().get(args.senderName); + const data = { + m: args.message ?? '', + }; + + return this.client + .exchanges() + .createExchangeMessage( + hab, + '/ipex/offer', + data, + { acdc: [args.acdc, undefined] }, + undefined, + args.datetime, + args.apply, + ) + } + + async submitOffer( + name: string, + exn: Serder, + sigs: string[], + atc: string, + recp: string[] + ): Promise { + const body = { + exn: exn.ked, + sigs, + atc, + rec: recp, + }; + + const response = await this.client.fetch( + `/identifiers/${name}/ipex/offer`, + 'POST', + body, + ); + + return response.json(); + } + + /** + * Create an IPEX agree EXN message + */ + async agree(args: IpexAgreeArgs): Promise<[Serder, string[], string]> { + const hab = await this.client.identifiers().get(args.senderName); + const data = { + m: args.message ?? '', + }; + + return this.client + .exchanges() + .createExchangeMessage( + hab, + '/ipex/agree', + data, + {}, + undefined, + args.datetime, + args.offer, + ) + } + + async submitAgree( + name: string, + exn: Serder, + sigs: string[], + recp: string[] + ): Promise { + const body = { + exn: exn.ked, + sigs, + rec: recp, + }; + + const response = await this.client.fetch( + `/identifiers/${name}/ipex/agree`, + 'POST', + body, + ); + + return response.json(); + } /** * Create an IPEX grant EXN message */ diff --git a/test/app/credentialing.test.ts b/test/app/credentialing.test.ts index e426a041..4dc70116 100644 --- a/test/app/credentialing.test.ts +++ b/test/app/credentialing.test.ts @@ -324,7 +324,7 @@ describe('Credentialing', () => { }); describe('Ipex', () => { - it('should do all the IPEX things', async () => { + it('IPEX - grant-admit flow initiated by discloser', async () => { await libsodium.ready; const bran = '0123456789abcdefghijk'; const client = new SignifyClient(url, bran, Tier.low, boot_url); @@ -359,7 +359,7 @@ describe('Ipex', () => { version: undefined, kind: undefined, }); - + const [grant, gsigs, end] = await ipex.grant({ senderName: 'multisig', recipient: holder, @@ -485,4 +485,361 @@ describe('Ipex', () => { assert.equal(aend, ''); }); + + it('IPEX - apply-admit flow initiated by disclosee', async () => { + await libsodium.ready; + const bran = '0123456789abcdefghijk'; + const client = new SignifyClient(url, bran, Tier.low, boot_url); + + await client.boot(); + await client.connect(); + + const ipex = client.ipex(); + + const holder = 'ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k'; + const [_, acdc] = Saider.saidify(mockCredential.sad); + + // Create iss + const vs = versify(Ident.KERI, undefined, Serials.JSON, 0); + const _iss = { + v: vs, + t: Ilks.iss, + d: '', + i: mockCredential.sad.d, + s: '0', + ri: mockCredential.sad.ri, + dt: mockCredential.sad.a.dt, + }; + + const [issSaider, iss] = Saider.saidify(_iss); + const iserder = new Serder(iss); + const anc = interact({ + pre: mockCredential.sad.i, + sn: 1, + data: [{}], + dig: mockCredential.sad.d, + version: undefined, + kind: undefined, + }); + + const [apply, applySigs, applyEnd] = await ipex.apply({ + senderName: 'multisig', + recipient: holder, + message: 'Applying', + schema: mockCredential.sad.s, + attributes: { LEI: mockCredential.sad.a.LEI }, + datetime: mockCredential.sad.a.dt, + }); + + assert.deepStrictEqual(apply.ked, { + v: 'KERI10JSON000176_', + t: 'exn', + d: 'EBbgtaWX8DYihokxDbq0k0RFjaQM7VgH3_88y3grPNsh', + i: 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', + p: '', + dt: '2023-08-23T15:16:07.553000+00:00', + r: '/ipex/apply', + q: {}, + a: { + m: 'Applying', + i: 'ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k', + s: 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', + a: { LEI: '5493001KJTIIGC8Y1R17' } + }, + e: {}, + }); + + assert.deepStrictEqual(applySigs, [ + 'AAADMrTKGghic8ye_BSoo1I3G5oit5GXvo3RXXTerZQCYtA4tYbji0bURgQII_ACT8louPDUgpWPQ_WscIa4v64I', + ]); + + assert.equal( + applyEnd, + '' + ); + + await ipex.submitApply('multisig', apply, applySigs, [holder]); + let lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal( + lastCall[0], + 'http://127.0.0.1:3901/identifiers/multisig/ipex/apply' + ); + + const [offer, offerSigs, offerEnd] = await ipex.offer({ + senderName: 'multisig', + recipient: holder, + message: 'How about this', + acdc: new Serder(acdc), + datetime: mockCredential.sad.a.dt, + apply: apply.ked.d, + }); + + assert.deepStrictEqual(offer.ked, { + v: 'KERI10JSON0002f0_', + t: 'exn', + d: 'EIOCPiCWlfxq4XnqZDaLjx4IOT4AIYqut3dKKIw5xnIa', + i: 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', + p: 'EBbgtaWX8DYihokxDbq0k0RFjaQM7VgH3_88y3grPNsh', + dt: '2023-08-23T15:16:07.553000+00:00', + r: '/ipex/offer', + q: {}, + a: { + m: 'How about this', + }, + e: { + acdc: { + v: 'ACDC10JSON000197_', + d: 'EMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo', + i: 'EMQQpnSkgfUOgWdzQTWfrgiVHKIDAhvAZIPQ6z3EAfz1', + ri: 'EGK216v1yguLfex4YRFnG7k1sXRjh3OKY7QqzdKsx7df', + s: 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', + a: { + d: 'EK0GOjijKd8_RLYz9qDuuG29YbbXjU8yJuTQanf07b6P', + i: 'EKvn1M6shPLnXTb47bugVJblKMuWC0TcLIePP8p98Bby', + dt: '2023-08-23T15:16:07.553000+00:00', + LEI: '5493001KJTIIGC8Y1R17', + }, + }, + d: 'EK72JZyOyz81Jvt--iebptfhIWiw2ZdQg7ondKd-EyJF', + }, + }); + + assert.deepStrictEqual(offerSigs, [ + 'AADWWHG9LHz5sLeE98HnsbYWyVl7zO4z4FOyWVyjOyWT5-VTa1ZTYRddScEmyOcG-E9XK0XSWe-cM5L3GJqpn5cN', + ]); + assert.equal(offerEnd, ''); + + await ipex.submitOffer('multisig', offer, offerSigs, offerEnd, [holder]); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal( + lastCall[0], + 'http://127.0.0.1:3901/identifiers/multisig/ipex/offer' + ); + + const [agree, agreeSigs, agreeEnd] = await ipex.agree({ + senderName: 'multisig', + recipient: holder, + message: 'OK!', + datetime: mockCredential.sad.a.dt, + offer: offer.ked.d, + }); + + assert.deepStrictEqual(agree.ked, { + v: 'KERI10JSON000114_', + t: 'exn', + d: 'ECVqeKGCclO-u0DEbJQoRwepE9RKGZRo_zArRgGpdZbI', + i: 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', + p: 'EIOCPiCWlfxq4XnqZDaLjx4IOT4AIYqut3dKKIw5xnIa', + dt: '2023-08-23T15:16:07.553000+00:00', + r: '/ipex/agree', + q: {}, + a: { + m: 'OK!', + }, + e: {}, + }); + + assert.deepStrictEqual(agreeSigs, [ + 'AADPaleEvvzOop-8iBDh3LWcjVzl0QzLL6UWYd0xTO02hWEIGVYGalKk1DYJFzwarMf6bAmDA1Betp_dyRNmT38N', + ]); + assert.equal(agreeEnd, ''); + + await ipex.submitAgree('multisig', agree, agreeSigs, [holder]); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal( + lastCall[0], + 'http://127.0.0.1:3901/identifiers/multisig/ipex/agree' + ); + + const [grant, gsigs, end] = await ipex.grant({ + senderName: 'multisig', + recipient: holder, + message: '', + acdc: new Serder(acdc), + iss: iserder, + anc, + datetime: mockCredential.sad.a.dt, + agree: agree.ked.d, + }); + + assert.deepStrictEqual(grant.ked, { + v: 'KERI10JSON0004dd_', + t: 'exn', + d: 'EDQ6lmaSIJxFPv7McRa69ljVtSrcY3cS8YYJgpn8DdhX', + i: 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', + p: 'ECVqeKGCclO-u0DEbJQoRwepE9RKGZRo_zArRgGpdZbI', + dt: '2023-08-23T15:16:07.553000+00:00', + r: '/ipex/grant', + q: {}, + a: { m: '', i: 'ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k' }, + e: { + acdc: { + v: 'ACDC10JSON000197_', + d: 'EMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo', + i: 'EMQQpnSkgfUOgWdzQTWfrgiVHKIDAhvAZIPQ6z3EAfz1', + ri: 'EGK216v1yguLfex4YRFnG7k1sXRjh3OKY7QqzdKsx7df', + s: 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', + a: { + d: 'EK0GOjijKd8_RLYz9qDuuG29YbbXjU8yJuTQanf07b6P', + i: 'EKvn1M6shPLnXTb47bugVJblKMuWC0TcLIePP8p98Bby', + dt: '2023-08-23T15:16:07.553000+00:00', + LEI: '5493001KJTIIGC8Y1R17', + }, + }, + iss: { + v: 'KERI10JSON0000ed_', + t: 'iss', + d: 'ENf3IEYwYtFmlq5ZzoI-zFzeR7E3ZNRN2YH_0KAFbdJW', + i: 'EMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo', + s: '0', + ri: 'EGK216v1yguLfex4YRFnG7k1sXRjh3OKY7QqzdKsx7df', + dt: '2023-08-23T15:16:07.553000+00:00', + }, + anc: { + v: 'KERI10JSON0000cd_', + t: 'ixn', + d: 'ECVCyxNpB4PJkpLbWqI02WXs1wf7VUxPNY2W28SN2qqm', + i: 'EMQQpnSkgfUOgWdzQTWfrgiVHKIDAhvAZIPQ6z3EAfz1', + s: '1', + p: 'EMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo', + a: [{}], + }, + d: 'EGpSjqjavdzgjQiyt0AtrOutWfKrj5gR63lOUUq-1sL-', + }, + }); + + assert.deepStrictEqual(gsigs, [ + 'AAC1EXfIqOP25RQYZocaXyTDDMykxNxV5a926DC74jhEqDkWseHCWaW5BZb-vNMfcR2n0zdaptQ-QbWxKKf3QqsB', + ]); + assert.equal( + end, + '-LAg4AACA-e-acdc-IABEMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo0AAAAAAAAAAAAAAAAAAAAAAAENf3IEYwYtFmlq5Zz' + + 'oI-zFzeR7E3ZNRN2YH_0KAFbdJW-LAW5AACAA-e-iss-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAAAECVCyxNpB4PJkpLbWqI02WXs1wf7VU' + + 'xPNY2W28SN2qqm-LAa5AACAA-e-anc-AABAADMtDfNihvCSXJNp1VronVojcPGo--0YZ4Kh6CAnowRnn4Or4FgZQqaqCEv6XVS413qfZo' + + 'Vp8j2uxTTPkItO7ED' + ); + + const [ng, ngsigs, ngend] = await ipex.grant({ + senderName: 'multisig', + recipient: holder, + message: '', + acdc: new Serder(acdc), + acdcAttachment: d(serializeACDCAttachment(iserder)), + iss: iserder, + issAttachment: d(serializeIssExnAttachment(anc)), + anc, + ancAttachment: + '-AABAADMtDfNihvCSXJNp1VronVojcPGo--0YZ4Kh6CAnowRnn4Or4FgZQqaqCEv6XVS413qfZoVp8j2uxTTPkItO7ED', + datetime: mockCredential.sad.a.dt, + agree: agree.ked.d, + }); + + assert.deepStrictEqual(ng.ked, grant.ked); + assert.deepStrictEqual(ngsigs, gsigs); + assert.deepStrictEqual(ngend, ngend); + + await ipex.submitGrant('multisig', ng, ngsigs, ngend, [holder]); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal( + lastCall[0], + 'http://127.0.0.1:3901/identifiers/multisig/ipex/grant' + ); + + const [admit, asigs, aend] = await ipex.admit( + 'holder', + '', + grant.ked.d, + mockCredential.sad.a.dt + ); + + assert.deepStrictEqual(admit.ked, { + v: 'KERI10JSON000111_', + t: 'exn', + d: 'ELMGqQV8nYJnrgjbJAKW3wmuhqzq5WL9gSvSP_d8wlYH', + i: 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', + p: 'EDQ6lmaSIJxFPv7McRa69ljVtSrcY3cS8YYJgpn8DdhX', + dt: '2023-08-23T15:16:07.553000+00:00', + r: '/ipex/admit', + q: {}, + a: { m: '' }, + e: {}, + }); + + assert.deepStrictEqual(asigs, [ + 'AADLGgf7zJ_Cxn86LOw9UWj_2YEXzRObJMssrdhH772ZOZU2D5PBDwV4-4-DLH24foLuT_dQmkwj70WlOWgL284F', + ]); + + await ipex.submitAdmit('multisig', admit, asigs, aend, [holder]); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal( + lastCall[0], + 'http://127.0.0.1:3901/identifiers/multisig/ipex/admit' + ); + + assert.equal(aend, ''); + }); + + it('IPEX - discloser can create an offer without apply', async () => { + await libsodium.ready; + const bran = '0123456789abcdefghijk'; + const client = new SignifyClient(url, bran, Tier.low, boot_url); + + await client.boot(); + await client.connect(); + + const ipex = client.ipex(); + + const holder = 'ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k'; + const [_, acdc] = Saider.saidify(mockCredential.sad); + + const [offer, offerSigs, offerEnd] = await ipex.offer({ + senderName: 'multisig', + recipient: holder, + message: 'Offering this', + acdc: new Serder(acdc), + datetime: mockCredential.sad.a.dt, + }); + + assert.deepStrictEqual(offer.ked, { + v: 'KERI10JSON0002c3_', + t: 'exn', + d: 'EPRsOfUsy_Wlrv49K1IvoaW2KYa701dRAqFsjP11HfnH', + i: 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', + p: '', + dt: '2023-08-23T15:16:07.553000+00:00', + r: '/ipex/offer', + q: {}, + a: { + m: 'Offering this', + }, + e: { + acdc: { + v: 'ACDC10JSON000197_', + d: 'EMwcsEMUEruPXVwPCW7zmqmN8m0I3CihxolBm-RDrsJo', + i: 'EMQQpnSkgfUOgWdzQTWfrgiVHKIDAhvAZIPQ6z3EAfz1', + ri: 'EGK216v1yguLfex4YRFnG7k1sXRjh3OKY7QqzdKsx7df', + s: 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', + a: { + d: 'EK0GOjijKd8_RLYz9qDuuG29YbbXjU8yJuTQanf07b6P', + i: 'EKvn1M6shPLnXTb47bugVJblKMuWC0TcLIePP8p98Bby', + dt: '2023-08-23T15:16:07.553000+00:00', + LEI: '5493001KJTIIGC8Y1R17', + }, + }, + d: 'EK72JZyOyz81Jvt--iebptfhIWiw2ZdQg7ondKd-EyJF', + }, + }); + + assert.deepStrictEqual(offerSigs, [ + 'AAAnd2tFMyrr0O91ilWCr6ae1UCK69k5_B4L6rrLTEzYF-Nw7rTVeveRaR-i4VL3An3yOsLaAupD0uHyyXkQWmIN', + ]); + assert.equal(offerEnd, ''); + + await ipex.submitOffer('multisig', offer, offerSigs, offerEnd, [holder]); + let lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal( + lastCall[0], + 'http://127.0.0.1:3901/identifiers/multisig/ipex/offer' + ); + }); }); From e27005a8a36c5c36935b0a5e116fb233fba9a5b3 Mon Sep 17 00:00:00 2001 From: iFergal Date: Thu, 21 Mar 2024 15:13:19 +0000 Subject: [PATCH 2/6] refactor: prettier run --- .../integration-scripts/credentials.test.ts | 26 ++++++++++----- src/keri/app/credentialing.ts | 20 +++++------ test/app/credentialing.test.ts | 33 ++++++++++--------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/examples/integration-scripts/credentials.test.ts b/examples/integration-scripts/credentials.test.ts index 52f3743d..93ec4205 100644 --- a/examples/integration-scripts/credentials.test.ts +++ b/examples/integration-scripts/credentials.test.ts @@ -290,14 +290,14 @@ test('single signature credentials', async () => { const [apply, sigs, _] = await verifierClient.ipex().apply({ senderName: verifierAid.name, schema: QVI_SCHEMA_SAID, - attributes: { LEI: "5493001KJTIIGC8Y1R17" }, + attributes: { LEI: '5493001KJTIIGC8Y1R17' }, recipient: holderAid.prefix, datetime: createTimestamp(), }); - const op = await verifierClient.ipex().submitApply( - verifierAid.name, apply, sigs, [holderAid.prefix] - ); + const op = await verifierClient + .ipex() + .submitApply(verifierAid.name, apply, sigs, [holderAid.prefix]); await waitOperation(verifierClient, op); }); @@ -331,7 +331,11 @@ test('single signature credentials', async () => { datetime: createTimestamp(), }); - const op = await holderClient.ipex().submitOffer(holderAid.name, offer, sigs, end, [verifierAid.prefix]); + const op = await holderClient + .ipex() + .submitOffer(holderAid.name, offer, sigs, end, [ + verifierAid.prefix, + ]); await waitOperation(holderClient, op); }); @@ -344,11 +348,13 @@ test('single signature credentials', async () => { const verifierOfferNote = verifierNotifications[0]; assert(verifierOfferNote.a.d); - const offer = await verifierClient.exchanges().get(verifierOfferNote.a.d); + const offer = await verifierClient + .exchanges() + .get(verifierOfferNote.a.d); offerSaid = offer.exn.d; expect(offer.exn.p).toBe(applySaid); - expect(offer.exn.e.acdc.a.LEI).toBe("5493001KJTIIGC8Y1R17"); + expect(offer.exn.e.acdc.a.LEI).toBe('5493001KJTIIGC8Y1R17'); await markAndRemoveNotification(verifierClient, verifierOfferNote); @@ -358,8 +364,10 @@ test('single signature credentials', async () => { offer: offerSaid, datetime: createTimestamp(), }); - - const op = await verifierClient.ipex().submitAgree(verifierAid.name, agree, sigs, [holderAid.prefix]); + + const op = await verifierClient + .ipex() + .submitAgree(verifierAid.name, agree, sigs, [holderAid.prefix]); await waitOperation(verifierClient, op); }); diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 5c2c31b6..486c2620 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -803,7 +803,7 @@ export class Ipex { a: args.attributes ?? {}, i: args.recipient, }; - + return this.client .exchanges() .createExchangeMessage( @@ -813,7 +813,7 @@ export class Ipex { {}, undefined, args.datetime, - undefined, + undefined ); } @@ -856,10 +856,10 @@ export class Ipex { { acdc: [args.acdc, undefined] }, undefined, args.datetime, - args.apply, - ) + args.apply + ); } - + async submitOffer( name: string, exn: Serder, @@ -877,7 +877,7 @@ export class Ipex { const response = await this.client.fetch( `/identifiers/${name}/ipex/offer`, 'POST', - body, + body ); return response.json(); @@ -901,10 +901,10 @@ export class Ipex { {}, undefined, args.datetime, - args.offer, - ) + args.offer + ); } - + async submitAgree( name: string, exn: Serder, @@ -920,7 +920,7 @@ export class Ipex { const response = await this.client.fetch( `/identifiers/${name}/ipex/agree`, 'POST', - body, + body ); return response.json(); diff --git a/test/app/credentialing.test.ts b/test/app/credentialing.test.ts index 4dc70116..cf939ae1 100644 --- a/test/app/credentialing.test.ts +++ b/test/app/credentialing.test.ts @@ -359,7 +359,7 @@ describe('Ipex', () => { version: undefined, kind: undefined, }); - + const [grant, gsigs, end] = await ipex.grant({ senderName: 'multisig', recipient: holder, @@ -544,7 +544,7 @@ describe('Ipex', () => { m: 'Applying', i: 'ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k', s: 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', - a: { LEI: '5493001KJTIIGC8Y1R17' } + a: { LEI: '5493001KJTIIGC8Y1R17' }, }, e: {}, }); @@ -553,10 +553,7 @@ describe('Ipex', () => { 'AAADMrTKGghic8ye_BSoo1I3G5oit5GXvo3RXXTerZQCYtA4tYbji0bURgQII_ACT8louPDUgpWPQ_WscIa4v64I', ]); - assert.equal( - applyEnd, - '' - ); + assert.equal(applyEnd, ''); await ipex.submitApply('multisig', apply, applySigs, [holder]); let lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; @@ -573,7 +570,7 @@ describe('Ipex', () => { datetime: mockCredential.sad.a.dt, apply: apply.ked.d, }); - + assert.deepStrictEqual(offer.ked, { v: 'KERI10JSON0002f0_', t: 'exn', @@ -603,18 +600,20 @@ describe('Ipex', () => { d: 'EK72JZyOyz81Jvt--iebptfhIWiw2ZdQg7ondKd-EyJF', }, }); - + assert.deepStrictEqual(offerSigs, [ 'AADWWHG9LHz5sLeE98HnsbYWyVl7zO4z4FOyWVyjOyWT5-VTa1ZTYRddScEmyOcG-E9XK0XSWe-cM5L3GJqpn5cN', ]); assert.equal(offerEnd, ''); - - await ipex.submitOffer('multisig', offer, offerSigs, offerEnd, [holder]); + + await ipex.submitOffer('multisig', offer, offerSigs, offerEnd, [ + holder, + ]); lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; assert.equal( lastCall[0], 'http://127.0.0.1:3901/identifiers/multisig/ipex/offer' - ); + ); const [agree, agreeSigs, agreeEnd] = await ipex.agree({ senderName: 'multisig', @@ -623,7 +622,7 @@ describe('Ipex', () => { datetime: mockCredential.sad.a.dt, offer: offer.ked.d, }); - + assert.deepStrictEqual(agree.ked, { v: 'KERI10JSON000114_', t: 'exn', @@ -638,12 +637,12 @@ describe('Ipex', () => { }, e: {}, }); - + assert.deepStrictEqual(agreeSigs, [ 'AADPaleEvvzOop-8iBDh3LWcjVzl0QzLL6UWYd0xTO02hWEIGVYGalKk1DYJFzwarMf6bAmDA1Betp_dyRNmT38N', ]); assert.equal(agreeEnd, ''); - + await ipex.submitAgree('multisig', agree, agreeSigs, [holder]); lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; assert.equal( @@ -834,8 +833,10 @@ describe('Ipex', () => { 'AAAnd2tFMyrr0O91ilWCr6ae1UCK69k5_B4L6rrLTEzYF-Nw7rTVeveRaR-i4VL3An3yOsLaAupD0uHyyXkQWmIN', ]); assert.equal(offerEnd, ''); - - await ipex.submitOffer('multisig', offer, offerSigs, offerEnd, [holder]); + + await ipex.submitOffer('multisig', offer, offerSigs, offerEnd, [ + holder, + ]); let lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; assert.equal( lastCall[0], From 5409fea8b5523729b288f1cd1eaa09418803d5dd Mon Sep 17 00:00:00 2001 From: iFergal Date: Thu, 9 May 2024 15:51:21 +0100 Subject: [PATCH 3/6] build: use latest dev docker build in cicd --- .github/workflows/main.yml | 2 +- docker-compose.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1744229..9464a18c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: strategy: matrix: os: ['ubuntu-latest'] - keria-version: ['latest'] + keria-version: ['0.2.0-dev0'] node-version: ['20'] env: KERIA_IMAGE_TAG: ${{ matrix.keria-version }} diff --git a/docker-compose.yaml b/docker-compose.yaml index 1fb5e6ab..5779e469 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -26,7 +26,7 @@ services: - 7723:7723 keria: - image: ${KERIA_IMAGE:-weboftrust/keria}:${KERIA_IMAGE_TAG:-latest} + image: ${KERIA_IMAGE:-weboftrust/keria}:${KERIA_IMAGE_TAG:-0.2.0-dev0} environment: - KERI_AGENT_CORS=1 - KERI_URL=http://keria:3902 From e6fb785833098f451950b54849503aad0858f8f1 Mon Sep 17 00:00:00 2001 From: iFergal Date: Fri, 17 May 2024 16:19:15 +0100 Subject: [PATCH 4/6] fix: adjustments to KERIA API [skip ci] --- examples/integration-scripts/salty.test.ts | 6 +++--- .../test-setup-single-client.test.ts | 7 ++----- src/keri/app/aiding.ts | 18 ++++++------------ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/examples/integration-scripts/salty.test.ts b/examples/integration-scripts/salty.test.ts index 95b88d19..39be3d73 100644 --- a/examples/integration-scripts/salty.test.ts +++ b/examples/integration-scripts/salty.test.ts @@ -156,13 +156,13 @@ test('salty', async () => { const events = client1.keyEvents(); const log = await events.get(aid['prefix']); assert.equal(log.length, 3); - let serder = new signify.Serder(log[0]); + let serder = new signify.Serder(log[0].ked); assert.equal(serder.pre, icp.pre); assert.equal(serder.ked['d'], icp.ked['d']); - serder = new signify.Serder(log[1]); + serder = new signify.Serder(log[1].ked); assert.equal(serder.pre, rot.pre); assert.equal(serder.ked['d'], rot.ked['d']); - serder = new signify.Serder(log[2]); + serder = new signify.Serder(log[2].ked); assert.equal(serder.pre, ixn.pre); assert.equal(serder.ked['d'], ixn.ked['d']); diff --git a/examples/integration-scripts/test-setup-single-client.test.ts b/examples/integration-scripts/test-setup-single-client.test.ts index cf2f941b..bed582a0 100644 --- a/examples/integration-scripts/test-setup-single-client.test.ts +++ b/examples/integration-scripts/test-setup-single-client.test.ts @@ -19,9 +19,6 @@ afterAll(async () => { describe('test-setup-single-client', () => { test('step1', async () => { - expect(client.agent?.pre).toEqual( - 'EC60ue9GOpQGrLBlS9T0dO6JkBTbv3V05Y4O730QBBoc' - ); expect(client.controller?.pre).toEqual( 'EB3UGWwIMq7ppzcQ697ImQIuXlBG5jzh-baSx-YG3-tY' ); @@ -33,7 +30,7 @@ describe('test-setup-single-client', () => { switch (env.preset) { case 'local': expect(name1_oobi).toEqual( - `http://127.0.0.1:3902/oobi/${name1_id}/agent/EC60ue9GOpQGrLBlS9T0dO6JkBTbv3V05Y4O730QBBoc` + `http://127.0.0.1:3902/oobi/${name1_id}/agent/${client.agent?.pre}` ); expect(oobi.oobis[0]).toEqual( `http://127.0.0.1:5642/oobi/${name1_id}/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha` @@ -47,7 +44,7 @@ describe('test-setup-single-client', () => { break; case 'docker': expect(name1_oobi).toEqual( - `http://keria:3902/oobi/${name1_id}/agent/EC60ue9GOpQGrLBlS9T0dO6JkBTbv3V05Y4O730QBBoc` + `http://keria:3902/oobi/${name1_id}/agent/${client.agent?.pre}` ); expect(oobi.oobis[0]).toEqual( `http://witness-demo:5642/oobi/${name1_id}/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha` diff --git a/src/keri/app/aiding.ts b/src/keri/app/aiding.ts index 011848fa..02a1b1ac 100644 --- a/src/keri/app/aiding.ts +++ b/src/keri/app/aiding.ts @@ -227,14 +227,8 @@ export class Identifier { icp: serder.ked, sigs: sigs, proxy: proxy, - smids: - states != undefined - ? states.map((state) => state.i) - : undefined, - rmids: - rstates != undefined - ? rstates.map((state) => state.i) - : undefined, + smids: states, + rmids: rstates, }; jsondata[algo] = keeper.params(); @@ -278,8 +272,8 @@ export class Identifier { jsondata[keeper.algo] = keeper.params(); const res = await this.client.fetch( - '/identifiers/' + name + '?type=ixn', - 'PUT', + '/identifiers/' + name + '/events', + 'POST', jsondata ); return new EventResult(serder, sigs, res); @@ -376,8 +370,8 @@ export class Identifier { jsondata[keeper.algo] = keeper.params(); const res = await this.client.fetch( - '/identifiers/' + name, - 'PUT', + '/identifiers/' + name + '/events', + 'POST', jsondata ); return new EventResult(serder, sigs, res); From b54026bc67a2bbe9c059b8d14e4d912eb420d479 Mon Sep 17 00:00:00 2001 From: iFergal Date: Sun, 19 May 2024 08:43:12 +0100 Subject: [PATCH 5/6] build: 0.2.0-dev1 keria tag --- .github/workflows/main.yml | 2 +- docker-compose.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8fbc2238..a26cc96d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,7 +55,7 @@ jobs: strategy: matrix: os: ['ubuntu-latest'] - keria-version: ['0.2.0-dev0'] + keria-version: ['0.2.0-dev1'] node-version: ['20'] env: KERIA_IMAGE_TAG: ${{ matrix.keria-version }} diff --git a/docker-compose.yaml b/docker-compose.yaml index 5779e469..6beb2588 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -26,7 +26,7 @@ services: - 7723:7723 keria: - image: ${KERIA_IMAGE:-weboftrust/keria}:${KERIA_IMAGE_TAG:-0.2.0-dev0} + image: ${KERIA_IMAGE:-weboftrust/keria}:${KERIA_IMAGE_TAG:-0.2.0-dev1} environment: - KERI_AGENT_CORS=1 - KERI_URL=http://keria:3902 From 7af874170110e64e3c529f209ff80da8aa27ed5d Mon Sep 17 00:00:00 2001 From: iFergal Date: Mon, 20 May 2024 09:12:03 +0100 Subject: [PATCH 6/6] test: fix aiding tests [skip ci] --- test/app/aiding.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/app/aiding.test.ts b/test/app/aiding.test.ts index 0872fc6c..9a579bd0 100644 --- a/test/app/aiding.test.ts +++ b/test/app/aiding.test.ts @@ -169,8 +169,8 @@ describe('Aiding', () => { await client.identifiers().rotate('aid1'); const lastCall = client.getLastMockRequest(); - assert.equal(lastCall.path, '/identifiers/aid1'); - assert.equal(lastCall.method, 'PUT'); + assert.equal(lastCall.path, '/identifiers/aid1/events'); + assert.equal(lastCall.method, 'POST'); assert.deepEqual(lastCall.body.rot, { v: 'KERI10JSON000160_', t: 'rot', @@ -215,8 +215,8 @@ describe('Aiding', () => { await client.identifiers().rotate('aid1'); const lastCall = client.getLastMockRequest(); - assert.equal(lastCall.path, '/identifiers/aid1'); - assert.equal(lastCall.method, 'PUT'); + assert.equal(lastCall.path, '/identifiers/aid1/events'); + assert.equal(lastCall.method, 'POST'); expect(lastCall.body.rot).toMatchObject({ v: 'KERI10JSON000160_', t: 'rot', @@ -241,8 +241,8 @@ describe('Aiding', () => { const lastCall = client.getLastMockRequest(); - expect(lastCall.path).toEqual('/identifiers/aid1?type=ixn'); - expect(lastCall.method).toEqual('PUT'); + expect(lastCall.path).toEqual('/identifiers/aid1/events'); + expect(lastCall.method).toEqual('POST'); expect(lastCall.body.ixn).toMatchObject({ v: 'KERI10JSON000138_', t: 'ixn', @@ -283,8 +283,8 @@ describe('Aiding', () => { const lastCall = client.getLastMockRequest(); - expect(lastCall.path).toEqual('/identifiers/aid1?type=ixn'); - expect(lastCall.method).toEqual('PUT'); + expect(lastCall.path).toEqual('/identifiers/aid1/events'); + expect(lastCall.method).toEqual('POST'); expect(lastCall.body.ixn).toMatchObject({ s: 'b', a: data,