From c9ea16d6004b0b249b7388a4db249db326f1c5a9 Mon Sep 17 00:00:00 2001 From: Patrick Vu Date: Wed, 12 Nov 2025 00:23:06 +0700 Subject: [PATCH 1/7] feat: agenting.py type hints and OpenAPI specs (#351) --- src/keri/app/coring.ts | 33 ++++--- src/keri/core/salter.ts | 1 - src/types/keria-api-schema.ts | 17 ++++ test-integration/multisig-join.test.ts | 15 +-- test-integration/multisig.test.ts | 125 +++++++++++++------------ test-integration/singlesig-ixn.test.ts | 16 +--- test-integration/singlesig-rot.test.ts | 23 ++--- test-integration/utils/test-util.ts | 3 +- test/app/delegating.test.ts | 2 +- test/app/registry.test.ts | 65 +++++++------ 10 files changed, 164 insertions(+), 136 deletions(-) diff --git a/src/keri/app/coring.ts b/src/keri/app/coring.ts index 0969de89..41fae8f4 100644 --- a/src/keri/app/coring.ts +++ b/src/keri/app/coring.ts @@ -5,6 +5,9 @@ import { Matter, MtrDex } from '../core/matter.ts'; import { components } from '../../types/keria-api-schema.ts'; type OOBI = components['schemas']['OOBI']; +type KeyState = components['schemas']['KeyStateRecord']; +type KeyEventRecord = components['schemas']['KeyEventRecord']; +type AgentConfig = components['schemas']['AgentConfig']; export function randomPasscode(): string { const raw = libsodium.randombytes_buf(16); @@ -34,7 +37,7 @@ export class Oobis { * Get the OOBI(s) for a managed indentifier for a given role * @param {string} name Name or alias of the identifier * @param {string} role Authorized role - * @returns {Promise} A promise to the OOBI(s) + * @returns {Promise} A promise to the OOBI(s) */ async get(name: string, role: string = 'agent'): Promise { const path = `/identifiers/${name}/oobis?role=${role}`; @@ -48,9 +51,9 @@ export class Oobis { * @async * @param {string} oobi The OOBI to be resolver * @param {string} [alias] Optional name or alias to link the OOBI resolution to a contact - * @returns {Promise} A promise to the long-running operation + * @returns {Promise>} A promise to the long-running operation */ - async resolve(oobi: string, alias?: string): Promise { + async resolve(oobi: string, alias?: string): Promise> { const path = `/oobis`; const data: any = { url: oobi, @@ -85,10 +88,6 @@ export interface OperationsDeps { ): Promise; } -export interface AgentConfig { - iurls?: string[]; -} - /** * Operations * @remarks @@ -123,7 +122,7 @@ export class Operations { * @param {string} type Select operations by type * @returns {Promise} A list of operations */ - async list(type?: string): Promise[]> { + async list(type?: string): Promise[]> { const params = new URLSearchParams(); if (type !== undefined) { params.append('type', type); @@ -208,9 +207,9 @@ export class KeyEvents { * Retrieve key events for an identifier * @async * @param {string} pre Identifier prefix - * @returns {Promise} A promise to the key events + * @returns {Promise} A promise to the key events */ - async get(pre: string): Promise { + async get(pre: string): Promise { const path = `/events?pre=${pre}`; const data = null; const method = 'GET'; @@ -236,9 +235,9 @@ export class KeyStates { * Retriene the key state for an identifier * @async * @param {string} pre Identifier prefix - * @returns {Promise} A promise to the key states + * @returns {Promise} A promise to the key states */ - async get(pre: string): Promise { + async get(pre: string): Promise { const path = `/states?pre=${pre}`; const data = null; const method = 'GET'; @@ -252,7 +251,7 @@ export class KeyStates { * @param {Array} pres List of identifier prefixes * @returns {Promise} A promise to the key states */ - async list(pres: string[]): Promise { + async list(pres: string[]): Promise { const path = `/states?${pres.map((pre) => `pre=${pre}`).join('&')}`; const data = null; const method = 'GET'; @@ -266,9 +265,13 @@ export class KeyStates { * @param {string} pre Identifier prefix * @param {number} [sn] Optional sequence number * @param {any} [anchor] Optional anchor - * @returns {Promise} A promise to the long-running operation + * @returns {Promise>} A promise to the long-running operation */ - async query(pre: string, sn?: string, anchor?: any): Promise { + async query( + pre: string, + sn?: string, + anchor?: any + ): Promise> { const path = `/queries`; const data: any = { pre: pre, diff --git a/src/keri/core/salter.ts b/src/keri/core/salter.ts index 61fbe8d1..46064a9e 100644 --- a/src/keri/core/salter.ts +++ b/src/keri/core/salter.ts @@ -4,7 +4,6 @@ import { Matter, MtrDex } from './matter.ts'; import { EmptyMaterialError } from './kering.ts'; import libsodium from 'libsodium-wrappers-sumo'; import { Tier } from '../../types/keria-api-schema.ts'; - export { Tier } from '../../types/keria-api-schema.ts'; interface SalterArgs { diff --git a/src/types/keria-api-schema.ts b/src/types/keria-api-schema.ts index 5360b47d..9df43863 100644 --- a/src/types/keria-api-schema.ts +++ b/src/types/keria-api-schema.ts @@ -540,6 +540,23 @@ export interface components { signing: components['schemas']['AidRecord'][]; rotation: components['schemas']['AidRecord'][]; }; + KeyEventRecord: { + ked: + | components['schemas']['ICP_V_1'] + | components['schemas']['ROT_V_1'] + | components['schemas']['IXN_V_1'] + | components['schemas']['DIP_V_1'] + | components['schemas']['DRT_V_1'] + | components['schemas']['ICP_V_2'] + | components['schemas']['ROT_V_2'] + | components['schemas']['IXN_V_2'] + | components['schemas']['DIP_V_2'] + | components['schemas']['DRT_V_2']; + atc: string; + }; + AgentConfig: { + iurls?: string[]; + }; }; responses: never; parameters: never; diff --git a/test-integration/multisig-join.test.ts b/test-integration/multisig-join.test.ts index 9968d893..7f6d65e1 100644 --- a/test-integration/multisig-join.test.ts +++ b/test-integration/multisig-join.test.ts @@ -1,4 +1,4 @@ -import signify, { Serder, SignifyClient } from 'signify-ts'; +import signify, { KeyState, Serder, SignifyClient } from 'signify-ts'; import { getOrCreateClient, getOrCreateIdentifier, @@ -213,8 +213,11 @@ describe('multisig-join', () => { waitOperation(client3, updates[5]), ]); - const states = [aid1State.response, aid2State.response]; - const rstates = [...states, aid3State.response]; + const states = [ + aid1State.response as KeyState, + aid2State.response as KeyState, + ]; + const rstates = [...states, aid3State.response as KeyState]; const rotateOperation1 = await client1 .identifiers() .rotate(nameMultisig, { states, rstates }); @@ -300,9 +303,9 @@ describe('multisig-join', () => { ]); const states = [ - aid1State.response, - aid2State.response, - aid3State.response, + aid1State.response as KeyState, + aid2State.response as KeyState, + aid3State.response as KeyState, ]; const rotateOperation1 = await client1 .identifiers() diff --git a/test-integration/multisig.test.ts b/test-integration/multisig.test.ts index e2ab2a76..6e680e5e 100644 --- a/test-integration/multisig.test.ts +++ b/test-integration/multisig.test.ts @@ -1,8 +1,15 @@ import { assert, test } from 'vitest'; -import signify, { +import { SignifyClient, Serder, IssueCredentialResult, + KeyState, + Algos, + Siger, + ready, + messagize, + b, + d, } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env.ts'; import { @@ -25,7 +32,7 @@ const SCHEMA_SAID = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao'; const SCHEMA_OOBI = `${vleiServerUrl}/oobi/${SCHEMA_SAID}`; test('multisig', async function run() { - await signify.ready(); + await ready(); // Boot Four clients const [client1, client2, client3, client4] = await Promise.all([ getOrCreateClient(), @@ -108,22 +115,26 @@ test('multisig', async function run() { op1 = await client1.challenges().verify(aid2.prefix, words); op1 = await waitOperation(client1, op1); console.log('Member1 verified challenge response from member2'); - let exnwords = new Serder(op1.response.exn); - op1 = await client1.challenges().responded(aid2.prefix, exnwords.sad.d); + let exnwords = new Serder((op1.response as any)?.exn); + let res1 = await client1 + .challenges() + .responded(aid2.prefix, exnwords.sad.d); + assert.equal(res1.status, 202); console.log('Member1 marked challenge response as accepted'); op1 = await client1.challenges().verify(aid3.prefix, words); op1 = await waitOperation(client1, op1); console.log('Member1 verified challenge response from member3'); - exnwords = new Serder(op1.response.exn); - op1 = await client1.challenges().responded(aid3.prefix, exnwords.sad.d); + exnwords = new Serder((op1.response as any)?.exn); + res1 = await client1.challenges().responded(aid3.prefix, exnwords.sad.d); + assert.equal(res1.status, 202); console.log('Member1 marked challenge response as accepted'); // First member start the creation of a multisig identifier let rstates = [aid1['state'], aid2['state'], aid3['state']]; let states = rstates; let icpResult1 = await client1.identifiers().create('multisig', { - algo: signify.Algos.group, + algo: Algos.group, mhab: aid1, isith: 3, nsith: 3, @@ -136,9 +147,9 @@ test('multisig', async function run() { let serder = icpResult1.serder; let sigs = icpResult1.sigs; - let sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + let sigers = sigs.map((sig) => new Siger({ qb64: sig })); - let ims = signify.d(signify.messagize(serder, sigers)); + let ims = d(messagize(serder, sigers)); let atc = ims.substring(serder.size); let embeds = { icp: [serder, atc], @@ -170,7 +181,7 @@ test('multisig', async function run() { let icp = exn.e.icp; let icpResult2 = await client2.identifiers().create('multisig', { - algo: signify.Algos.group, + algo: Algos.group, mhab: aid2, isith: icp.kt, nsith: icp.nt, @@ -182,9 +193,9 @@ test('multisig', async function run() { op2 = await icpResult2.op(); serder = icpResult2.serder; sigs = icpResult2.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); embeds = { icp: [serder, atc], @@ -214,7 +225,7 @@ test('multisig', async function run() { exn = res[0].exn; icp = exn.e.icp; let icpResult3 = await client3.identifiers().create('multisig', { - algo: signify.Algos.group, + algo: Algos.group, mhab: aid3, isith: icp.kt, nsith: icp.nt, @@ -226,9 +237,9 @@ test('multisig', async function run() { op3 = await icpResult3.op(); serder = icpResult3.serder; sigs = icpResult3.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); embeds = { icp: [serder, atc], @@ -327,10 +338,8 @@ test('multisig', async function run() { 'SealEvent', { i: hab['prefix'], s: mstate['ee']['s'], d: mstate['ee']['d'] }, ]; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); - let roleims = signify.d( - signify.messagize(rpy, sigers, seal, undefined, undefined, false) - ); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); + let roleims = d(messagize(rpy, sigers, seal, undefined, undefined, false)); atc = roleims.substring(rpy.size); let roleembeds = { rpy: [rpy, atc], @@ -375,10 +384,8 @@ test('multisig', async function run() { 'SealEvent', { i: hab['prefix'], s: mstate['ee']['s'], d: mstate['ee']['d'] }, ]; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); - roleims = signify.d( - signify.messagize(rpy, sigers, seal, undefined, undefined, false) - ); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); + roleims = d(messagize(rpy, sigers, seal, undefined, undefined, false)); atc = roleims.substring(rpy.size); roleembeds = { rpy: [rpy, atc], @@ -422,10 +429,8 @@ test('multisig', async function run() { 'SealEvent', { i: hab['prefix'], s: mstate['ee']['s'], d: mstate['ee']['d'] }, ]; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); - roleims = signify.d( - signify.messagize(rpy, sigers, seal, undefined, undefined, false) - ); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); + roleims = d(messagize(rpy, sigers, seal, undefined, undefined, false)); atc = roleims.substring(rpy.size); roleembeds = { rpy: [rpy, atc], @@ -470,9 +475,9 @@ test('multisig', async function run() { op1 = await eventResponse1.op(); serder = eventResponse1.serder; sigs = eventResponse1.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); let xembeds = { ixn: [serder, atc], @@ -510,9 +515,9 @@ test('multisig', async function run() { op2 = await icpResult2.op(); serder = icpResult2.serder; sigs = icpResult2.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); xembeds = { ixn: [serder, atc], @@ -548,9 +553,9 @@ test('multisig', async function run() { op3 = await icpResult3.op(); serder = icpResult3.serder; sigs = icpResult3.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); xembeds = { ixn: [serder, atc], @@ -600,16 +605,16 @@ test('multisig', async function run() { // Update new key states op1 = await client1.keyStates().query(aid2.prefix, '1'); op1 = await waitOperation(client1, op1); - const aid2State = op1['response']; + const aid2State = op1['response'] as KeyState; op1 = await client1.keyStates().query(aid3.prefix, '1'); op1 = await waitOperation(client1, op1); - const aid3State = op1['response']; + const aid3State = op1['response'] as KeyState; op2 = await client2.keyStates().query(aid3.prefix, '1'); op2 = await waitOperation(client2, op2); op2 = await client2.keyStates().query(aid1.prefix, '1'); op2 = await waitOperation(client2, op2); - const aid1State = op2['response']; + const aid1State = op2['response'] as KeyState; op3 = await client3.keyStates().query(aid1.prefix, '1'); op3 = await waitOperation(client3, op3); @@ -635,9 +640,9 @@ test('multisig', async function run() { op1 = await eventResponse1.op(); serder = eventResponse1.serder; sigs = eventResponse1.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); let rembeds = { rot: [serder, atc], @@ -675,9 +680,9 @@ test('multisig', async function run() { op2 = await icpResult2.op(); serder = icpResult2.serder; sigs = icpResult2.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); rembeds = { rot: [serder, atc], @@ -711,9 +716,9 @@ test('multisig', async function run() { op3 = await icpResult3.op(); serder = icpResult3.serder; sigs = icpResult3.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(serder, sigers)); + ims = d(messagize(serder, sigers)); atc = ims.substring(serder.size); rembeds = { rot: [serder, atc], @@ -762,9 +767,9 @@ test('multisig', async function run() { let anc = vcpRes1.serder; sigs = vcpRes1.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(anc, sigers)); + ims = d(messagize(anc, sigers)); atc = ims.substring(anc.size); let regbeds = { vcp: [serder, ''], @@ -804,9 +809,9 @@ test('multisig', async function run() { anc = vcpRes2.serder; sigs = vcpRes2.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(anc, sigers)); + ims = d(messagize(anc, sigers)); atc = ims.substring(anc.size); regbeds = { vcp: [serder, ''], @@ -846,9 +851,9 @@ test('multisig', async function run() { anc = vcpRes3.serder; sigs = vcpRes3.sigs; - sigers = sigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = sigs.map((sig) => new Siger({ qb64: sig })); - ims = signify.d(signify.messagize(anc, sigers)); + ims = d(messagize(anc, sigers)); atc = ims.substring(anc.size); regbeds = { vcp: [serder, ''], @@ -968,9 +973,9 @@ test('multisig', async function run() { 'SealEvent', { i: m['prefix'], s: mstate['ee']['s'], d: mstate['ee']['d'] }, ]; - sigers = gsigs.map((sig) => new signify.Siger({ qb64: sig })); + sigers = gsigs.map((sig) => new Siger({ qb64: sig })); - let gims = signify.d(signify.messagize(grant, sigers, seal)); + let gims = d(messagize(grant, sigers, seal)); atc = gims.substring(grant.size); atc += end; let gembeds = { @@ -1011,9 +1016,9 @@ test('multisig', async function run() { .ipex() .submitGrant('multisig', grant2, gsigs2, end2, [holder]); - sigers = gsigs2.map((sig) => new signify.Siger({ qb64: sig })); + sigers = gsigs2.map((sig) => new Siger({ qb64: sig })); - gims = signify.d(signify.messagize(grant2, sigers, seal)); + gims = d(messagize(grant2, sigers, seal)); atc = gims.substring(grant2.size); atc += end2; @@ -1053,9 +1058,9 @@ test('multisig', async function run() { .ipex() .submitGrant('multisig', grant3, gsigs3, end3, [holder]); - sigers = gsigs3.map((sig) => new signify.Siger({ qb64: sig })); + sigers = gsigs3.map((sig) => new Siger({ qb64: sig })); - gims = signify.d(signify.messagize(grant3, sigers, seal)); + gims = d(messagize(grant3, sigers, seal)); atc = gims.substring(grant3.size); atc += end3; @@ -1197,9 +1202,9 @@ async function multisigIssue( 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 sigs = await keeper.sign(b(result.anc.raw)); + const sigers = sigs.map((sig: string) => new Siger({ qb64: sig })); + const ims = d(messagize(result.anc, sigers)); const atc = ims.substring(result.anc.size); const embeds = { @@ -1237,9 +1242,9 @@ async function multisigRevoke( const members = await client.identifiers().members(groupName); const keeper = client.manager!.get(groupHab); - const sigs = await keeper.sign(signify.b(anc.raw)); - const sigers = sigs.map((sig: string) => new signify.Siger({ qb64: sig })); - const ims = signify.d(signify.messagize(anc, sigers)); + const sigs = await keeper.sign(b(anc.raw)); + const sigers = sigs.map((sig: string) => new Siger({ qb64: sig })); + const ims = d(messagize(anc, sigers)); const atc = ims.substring(anc.size); const embeds = { diff --git a/test-integration/singlesig-ixn.test.ts b/test-integration/singlesig-ixn.test.ts index aa7513c1..5d06966d 100644 --- a/test-integration/singlesig-ixn.test.ts +++ b/test-integration/singlesig-ixn.test.ts @@ -1,4 +1,4 @@ -import { EventResult, SignifyClient } from 'signify-ts'; +import { EventResult, SignifyClient, KeyState } from 'signify-ts'; import { assertOperations, getOrCreateClients, @@ -25,12 +25,6 @@ afterAll(async () => { await assertOperations(client1, client2); }); -interface KeyState { - i: string; - s: string; - [property: string]: any; -} - describe('singlesig-ixn', () => { test('step1', async () => { assert.equal(name1_id, contact1_id); @@ -48,7 +42,7 @@ describe('singlesig-ixn', () => { // local keystate before ixn const keystate0: KeyState = ( await client1.keyStates().get(name1_id) - ).at(0); + ).at(0)!; expect(keystate0).not.toBeNull(); // ixn @@ -60,7 +54,7 @@ describe('singlesig-ixn', () => { // local keystate after ixn const keystate1: KeyState = ( await client1.keyStates().get(name1_id) - ).at(0); + ).at(0)!; expect(parseInt(keystate1.s)).toBeGreaterThan(0); // sequence has incremented assert.equal(parseInt(keystate1.s), parseInt(keystate0.s) + 1); @@ -68,7 +62,7 @@ describe('singlesig-ixn', () => { // remote keystate after ixn const keystate2: KeyState = ( await client2.keyStates().get(contact1_id) - ).at(0); + ).at(0)!; // remote keystate is one behind assert.equal(parseInt(keystate2.s), parseInt(keystate1.s) - 1); @@ -77,7 +71,7 @@ describe('singlesig-ixn', () => { .keyStates() .query(contact1_id, keystate1.s, undefined); op = await waitOperation(client2, op); - const keystate3: KeyState = op.response; + const keystate3: KeyState = op.response as KeyState; // local and remote keystate match assert.equal(keystate3.s, keystate1.s); }); diff --git a/test-integration/singlesig-rot.test.ts b/test-integration/singlesig-rot.test.ts index cf07b070..8f73adc9 100644 --- a/test-integration/singlesig-rot.test.ts +++ b/test-integration/singlesig-rot.test.ts @@ -1,5 +1,10 @@ import { afterAll, assert, beforeAll, describe, expect, test } from 'vitest'; -import { EventResult, RotateIdentifierArgs, SignifyClient } from 'signify-ts'; +import { + EventResult, + RotateIdentifierArgs, + SignifyClient, + KeyState, +} from 'signify-ts'; import { assertOperations, getOrCreateClients, @@ -25,14 +30,6 @@ afterAll(async () => { await assertOperations(client1, client2); }); -interface KeyState { - i: string; - s: string; - k: string[]; - n: string[]; - [property: string]: any; -} - describe('singlesig-rot', () => { test('step1', async () => { assert.equal(name1_id, contact1_id); @@ -50,7 +47,7 @@ describe('singlesig-rot', () => { // local keystate before rot const keystate0: KeyState = ( await client1.keyStates().get(name1_id) - ).at(0); + ).at(0)!; expect(keystate0).not.toBeNull(); assert.strictEqual(keystate0.k.length, 1); assert.strictEqual(keystate0.n.length, 1); @@ -65,7 +62,7 @@ describe('singlesig-rot', () => { // local keystate after rot const keystate1: KeyState = ( await client1.keyStates().get(name1_id) - ).at(0); + ).at(0)!; expect(parseInt(keystate1.s)).toBeGreaterThan(0); // sequence has incremented assert.equal(parseInt(keystate1.s), parseInt(keystate0.s) + 1); @@ -77,7 +74,7 @@ describe('singlesig-rot', () => { // remote keystate after rot const keystate2: KeyState = ( await client2.keyStates().get(contact1_id) - ).at(0); + ).at(0)!; // remote keystate is one behind assert.equal(parseInt(keystate2.s), parseInt(keystate1.s) - 1); @@ -86,7 +83,7 @@ describe('singlesig-rot', () => { .keyStates() .query(contact1_id, keystate1.s, undefined); op = await waitOperation(client2, op); - const keystate3: KeyState = op.response; + const keystate3: KeyState = op.response as KeyState; // local and remote keystate match assert.equal(keystate3.s, keystate1.s); assert.equal(keystate3.k[0], keystate1.k[0]); diff --git a/test-integration/utils/test-util.ts b/test-integration/utils/test-util.ts index 54dfb760..ba30ed5c 100644 --- a/test-integration/utils/test-util.ts +++ b/test-integration/utils/test-util.ts @@ -250,7 +250,8 @@ export async function getOrCreateContact( } let op = await client.oobis().resolve(oobi, name); op = await waitOperation(client, op); - return op.response.i; + const response = op.response as { i: string }; + return response.i; } /** diff --git a/test/app/delegating.test.ts b/test/app/delegating.test.ts index 48bff125..db886b01 100644 --- a/test/app/delegating.test.ts +++ b/test/app/delegating.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from 'vitest'; -import { Tier } from '../../src/index.ts'; +import { Tier } from '../../src/keri/core/salter.ts'; import libsodium from 'libsodium-wrappers-sumo'; import { SignifyClient } from '../../src/keri/app/clienting.ts'; import { createMockFetch } from './test-utils.ts'; diff --git a/test/app/registry.test.ts b/test/app/registry.test.ts index 8254fdc6..c81d4126 100644 --- a/test/app/registry.test.ts +++ b/test/app/registry.test.ts @@ -10,6 +10,29 @@ import { import { assert, describe, expect, it } from 'vitest'; import { HabState, KeyState } from '../../src/keri/core/keyState.ts'; +const keystate: KeyState = { + s: '0', + d: 'a digest', + i: '', + p: '', + f: '', + dt: '', + et: '', + kt: '', + k: [], + nt: '', + n: [], + bt: '', + b: [], + c: [], + ee: { + s: '', + d: '', + br: [], + ba: [], + }, + di: '', +}; describe('registry', () => { it('should create a registry', async () => { await libsodium.ready; @@ -17,11 +40,18 @@ describe('registry', () => { const mockedIdentifiers = mock(Identifier); const mockedKeyManager = mock(IdentifierManagerFactory); const mockedKeeper = mock(SaltyIdentifierManager); - - const hab = { + const hab: HabState = { + name: 'test-hab', prefix: 'hab prefix', - state: { s: '0', d: 'a digest' } as KeyState, - } as HabState; + icp_dt: '2023-12-01T10:05:25.062609+00:00', + state: keystate, + transferable: false, + windexes: [], + randy: { + prxs: [], + nxts: [], + }, + }; when(mockedClient.manager).thenReturn(instance(mockedKeyManager)); when(mockedKeyManager.get(hab)).thenReturn(instance(mockedKeeper)); @@ -66,30 +96,9 @@ describe('registry', () => { await libsodium.ready; const mockedClient = mock(SignifyClient); const mockedIdentifiers = mock(Identifier); - const keystate: KeyState = { - s: '0', - d: 'a digest', - i: '', - p: '', - f: '', - dt: '', - et: '', - kt: '', - k: [], - nt: '', - n: [], - bt: '', - b: [], - c: ['EO'], - ee: { - s: '', - d: '', - br: [], - ba: [], - }, - di: '', - }; - const hab = { + + keystate.c = ['EO']; + const hab: HabState = { prefix: 'hab prefix', state: keystate, name: 'a name', From 7f26038635a740deb819ed57a15c6f58d4d18936 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:43:02 -0700 Subject: [PATCH 2/7] chore(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 (#352) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.1.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6381d67c..68ace988 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2968,9 +2968,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { From 5e540e7af414ad3915afad79fc7cc207b8f2cfa4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 09:46:05 +0000 Subject: [PATCH 3/7] chore(deps-dev): bump glob from 10.4.5 to 10.5.0 (#354) Bumps [glob](https://github.com/isaacs/node-glob) from 10.4.5 to 10.5.0. - [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md) - [Commits](https://github.com/isaacs/node-glob/compare/v10.4.5...v10.5.0) --- updated-dependencies: - dependency-name: glob dependency-version: 10.5.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68ace988..c380ab75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1429,6 +1429,7 @@ "integrity": "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1486,6 +1487,7 @@ "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/types": "8.44.0", @@ -1853,6 +1855,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2301,6 +2304,7 @@ "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2657,9 +2661,9 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -4008,6 +4012,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4193,6 +4198,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4262,6 +4268,7 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -4398,6 +4405,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4411,6 +4419,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", From a76d23992fbce0493873b349f9ff241b4957e32a Mon Sep 17 00:00:00 2001 From: Patrick Vu Date: Fri, 27 Jun 2025 18:01:40 +0700 Subject: [PATCH 4/7] update TS types after changing End of these files: delegating.py, grouping.py, ipexing.py, notifying.py, exchanging.py --- scripts/generate-types.js | 2 +- src/keri/app/aiding.ts | 2 +- src/keri/app/credentialing.ts | 10 +- src/keri/app/exchanging.ts | 8 +- src/keri/app/grouping.ts | 17 +- src/keri/core/keyState.ts | 14 ++ src/types/keria-api-schema.ts | 182 +++++++++++++++++- test-integration/credentials.test.ts | 18 +- test-integration/multisig-holder.test.ts | 21 +- test-integration/multisig-join.test.ts | 25 ++- .../multisig-vlei-issuance.test.ts | 2 + test-integration/multisig.test.ts | 156 ++++++++++++--- test-integration/utils/multisig-utils.ts | 21 +- 13 files changed, 413 insertions(+), 65 deletions(-) diff --git a/scripts/generate-types.js b/scripts/generate-types.js index 67a646a6..7dd7a35a 100644 --- a/scripts/generate-types.js +++ b/scripts/generate-types.js @@ -27,7 +27,7 @@ const content = ast.filter((s) => { return false; }); -const header = `// AUTO-GENERATED: Only components retained from OpenAPI schema\n\n`; +const header = `// AUTO-GENERATED: Only components and enums retained from OpenAPI schema\n\n`; await writeFile(outputFile, `${header}${astToString(content)}`); console.log(`🚀 ${specUrl} → ${outputFile}`); diff --git a/src/keri/app/aiding.ts b/src/keri/app/aiding.ts index 51ba1303..63865e93 100644 --- a/src/keri/app/aiding.ts +++ b/src/keri/app/aiding.ts @@ -183,7 +183,7 @@ export class Identifier { if (!transferable) { ncount = 0; - nsith = 0; + nsith = '0'; dcode = MtrDex.Ed25519N; } diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 870f355e..e033eb71 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -781,7 +781,7 @@ export class Ipex { exn: Serder, sigs: string[], recp: string[] - ): Promise { + ): Promise> { const body = { exn: exn.sad, sigs, @@ -825,7 +825,7 @@ export class Ipex { sigs: string[], atc: string, recp: string[] - ): Promise { + ): Promise> { const body = { exn: exn.sad, sigs, @@ -869,7 +869,7 @@ export class Ipex { exn: Serder, sigs: string[], recp: string[] - ): Promise { + ): Promise> { const body = { exn: exn.sad, sigs, @@ -937,7 +937,7 @@ export class Ipex { sigs: string[], atc: string, recp: string[] - ): Promise { + ): Promise> { const body = { exn: exn.sad, sigs: sigs, @@ -982,7 +982,7 @@ export class Ipex { sigs: string[], atc: string, recp: string[] - ): Promise { + ): Promise> { const body = { exn: exn.sad, sigs: sigs, diff --git a/src/keri/app/exchanging.ts b/src/keri/app/exchanging.ts index 94946649..651e49b3 100644 --- a/src/keri/app/exchanging.ts +++ b/src/keri/app/exchanging.ts @@ -6,6 +6,10 @@ import { Pather } from '../core/pather.ts'; import { Counter, CtrDex } from '../core/counter.ts'; import { Saider } from '../core/saider.ts'; import { HabState } from '../core/keyState.ts'; +import { Exn } from './grouping.ts'; +import { components } from '../../types/keria-api-schema.ts'; + +export type ExchangeResource = components['schemas']['ExchangeResource']; /** * Exchanges @@ -101,7 +105,7 @@ export class Exchanges { /** * Send exn messaget to list of recipients * @async - * @returns {Promise} A promise to the list of replay messages + * @returns {Promise} A promise to the list of replay messages * @param name * @param topic * @param exn @@ -137,7 +141,7 @@ export class Exchanges { * @returns A promise to the exn message * @param said The said of the exn message */ - async get(said: string): Promise { + async get(said: string): Promise { const path = `/exchanges/${said}`; const method = 'GET'; const res = await this.client.fetch(path, method, null); diff --git a/src/keri/app/grouping.ts b/src/keri/app/grouping.ts index eef4b7c0..db5dfa42 100644 --- a/src/keri/app/grouping.ts +++ b/src/keri/app/grouping.ts @@ -1,5 +1,10 @@ import { SignifyClient } from './clienting.ts'; import { Dict } from '../core/core.ts'; +import { Operation } from './coring.ts'; +import { components } from '../../types/keria-api-schema.ts'; + +export type Exn = components['schemas']['Exn']; +export type ExnMultisig = components['schemas']['ExnMultisig']; /** * Groups @@ -19,9 +24,9 @@ export class Groups { * Get group request messages * @async * @param {string} [said] SAID of exn message to load - * @returns {Promise} A promise to the list of replay messages + * @returns {Promise} A promise to the list of replay messages */ - async getRequest(said: string): Promise { + async getRequest(said: string): Promise { const path = `/multisig/request/` + said; const method = 'GET'; const res = await this.client.fetch(path, method, null); @@ -35,14 +40,14 @@ export class Groups { * @param {Dict} [exn] exn message to send to other members * @param {string[]} [sigs] signature of the participant over the exn * @param {string} [atc] additional attachments from embedded events in exn - * @returns {Promise} A promise to the list of replay messages + * @returns {Promise} A promise to the list of replay messages */ async sendRequest( name: string, exn: Dict, sigs: string[], atc: string - ): Promise { + ): Promise { const path = `/identifiers/${name}/multisig/request`; const method = 'POST'; const data = { @@ -64,7 +69,7 @@ export class Groups { * @param {string} [gid] prefix * @param {string[]} [smids] array of particpants * @param {string[]} [rmids] array of particpants - * @returns {Promise} A promise to the list of replay messages + * @returns {Promise} A promise to the list of replay messages */ async join( name: string, @@ -73,7 +78,7 @@ export class Groups { gid: string, smids: string[], rmids: string[] - ): Promise { + ): Promise> { const path = `/identifiers/${name}/multisig/join`; const method = 'POST'; const data = { diff --git a/src/keri/core/keyState.ts b/src/keri/core/keyState.ts index cf26dba4..19745e7a 100644 --- a/src/keri/core/keyState.ts +++ b/src/keri/core/keyState.ts @@ -34,3 +34,17 @@ export type ExternState = components['schemas']['ExternState']; * Defining properties of an identifier habitat, know as a Hab in KERIpy. */ export type HabState = components['schemas']['Identifier']; + +export type Icp = + | components['schemas']['ICP_V_1'] + | components['schemas']['ICP_V_2']; +export type Ixn = + | components['schemas']['IXN_V_1'] + | components['schemas']['IXN_V_2']; +export type ExnV1 = + | components['schemas']['EXN_V_1'] + | components['schemas']['EXN_V_2']; +export type MultisigExnEmbeds = components['schemas']['MultisigExnEmbeds']; +export type MultisigInceptEmbeds = + components['schemas']['MultisigInceptEmbeds']; +export type MultisigRpyEmbeds = components['schemas']['MultisigRpyEmbeds']; diff --git a/src/types/keria-api-schema.ts b/src/types/keria-api-schema.ts index 9df43863..5638d920 100644 --- a/src/types/keria-api-schema.ts +++ b/src/types/keria-api-schema.ts @@ -1,4 +1,4 @@ -// AUTO-GENERATED: Only components retained from OpenAPI schema +// AUTO-GENERATED: Only components and enums retained from OpenAPI schema export interface components { schemas: { @@ -252,6 +252,49 @@ export interface components { r: string; a: unknown; }; + VCP_V_1: { + v: string; + t: string; + d: string; + i: string; + ii: string; + s: string; + c: string[]; + bt: string; + b: string[]; + n: string; + }; + EXN_V_1: { + v: string; + t: string; + d: string; + i: string; + rp: string; + p: string; + dt: string; + r: string; + q: { + [key: string]: unknown; + }; + a: unknown; + e: { + [key: string]: unknown; + }; + }; + EXN_V_2: { + v: string; + t: string; + d: string; + i: string; + x: string; + p: string; + dt: string; + r: string; + q: { + [key: string]: unknown; + }; + a: unknown; + }; Credential: { sad: | components['schemas']['ACDC_V_1'] @@ -542,20 +585,149 @@ export interface components { }; KeyEventRecord: { ked: + | components['schemas']['IXN_V_1'] + | components['schemas']['IXN_V_2'] | components['schemas']['ICP_V_1'] + | components['schemas']['ICP_V_2'] | components['schemas']['ROT_V_1'] - | components['schemas']['IXN_V_1'] + | components['schemas']['ROT_V_2'] | components['schemas']['DIP_V_1'] + | components['schemas']['DIP_V_2'] | components['schemas']['DRT_V_1'] + | components['schemas']['DRT_V_2']; + atc: string; + }; + AgentConfig: { + iurls?: string[]; + }; + Exn: + | components['schemas']['EXN_V_1'] + | components['schemas']['EXN_V_2']; + Icp: + | components['schemas']['ICP_V_1'] + | components['schemas']['ICP_V_2']; + Rot: + | components['schemas']['ROT_V_1'] + | components['schemas']['ROT_V_2']; + Vcp: components['schemas']['VCP_V_1']; + Iss: components['schemas']['ISS_V_1']; + Ixn: + | components['schemas']['IXN_V_1'] + | components['schemas']['IXN_V_2']; + NotificationData: { + r?: string; + d?: string; + m?: string; + } & { + [key: string]: unknown; + }; + Notification: { + i: string; + dt: string; + r: boolean; + a: components['schemas']['NotificationData']; + }; + ExchangeResource: { + exn: components['schemas']['Exn']; + pathed: { + [key: string]: unknown; + }; + }; + MultisigInceptEmbeds: { + icp: components['schemas']['Icp']; + rot?: components['schemas']['Rot']; + }; + MultisigRotateEmbeds: { + rot: unknown; + }; + MultisigInteractEmbeds: { + ixn: components['schemas']['Ixn']; + }; + MultisigRegistryInceptEmbeds: { + vcp: components['schemas']['Vcp']; + anc: + | components['schemas']['IXN_V_1'] + | components['schemas']['IXN_V_2'] + | components['schemas']['ICP_V_1'] | components['schemas']['ICP_V_2'] + | components['schemas']['ROT_V_1'] | components['schemas']['ROT_V_2'] + | components['schemas']['DIP_V_1'] + | components['schemas']['DIP_V_2'] + | components['schemas']['DRT_V_1'] + | components['schemas']['DRT_V_2']; + }; + ISS_V_1: { + v: string; + t: string; + d: string; + i: string; + s: string; + ri: string; + dt: string; + }; + MultisigIssueEmbeds: { + acdc: + | components['schemas']['ACDC_V_1'] + | components['schemas']['ACDC_V_2']; + iss: components['schemas']['Iss']; + anc: + | components['schemas']['IXN_V_1'] | components['schemas']['IXN_V_2'] + | components['schemas']['ICP_V_1'] + | components['schemas']['ICP_V_2'] + | components['schemas']['ROT_V_1'] + | components['schemas']['ROT_V_2'] + | components['schemas']['DIP_V_1'] | components['schemas']['DIP_V_2'] + | components['schemas']['DRT_V_1'] | components['schemas']['DRT_V_2']; - atc: string; }; - AgentConfig: { - iurls?: string[]; + REV_V_1: { + v: string; + t: string; + d: string; + i: string; + s: string; + ri: string; + p: string; + dt: string; + }; + MultisigRevokeEmbeds: { + rev: components['schemas']['REV_V_1']; + anc: unknown; + }; + MultisigRpyEmbeds: { + rpy: components['schemas']['Rpy']; + anc: unknown; + }; + MultisigExnEmbeds: { + exn: components['schemas']['Exn']; + }; + ExnEmbeds: { + d: string; + } & ( + | components['schemas']['MultisigInceptEmbeds'] + | components['schemas']['MultisigRotateEmbeds'] + | components['schemas']['MultisigInteractEmbeds'] + | components['schemas']['MultisigRegistryInceptEmbeds'] + | components['schemas']['MultisigIssueEmbeds'] + | components['schemas']['MultisigRevokeEmbeds'] + | components['schemas']['MultisigRpyEmbeds'] + | components['schemas']['MultisigExnEmbeds'] + ); + ExnMultisig: { + exn: components['schemas']['Exn']; + paths: { + [key: string]: unknown; + }; + e: components['schemas']['ExnEmbeds']; + /** @default null */ + groupName: string | null; + /** @default null */ + memberName: string | null; + /** @default null */ + sender: string | null; }; }; responses: never; diff --git a/test-integration/credentials.test.ts b/test-integration/credentials.test.ts index 98b58b4b..63367ff5 100644 --- a/test-integration/credentials.test.ts +++ b/test-integration/credentials.test.ts @@ -344,9 +344,9 @@ test('single signature credentials', { timeout: 90000 }, async () => { const apply = await holderClient.exchanges().get(holderApplyNote.a.d); applySaid = apply.exn.d; - const 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 filter: { [x: string]: any } = { '-s': (apply.exn.a as any).s }; + for (const key in (apply.exn.a as any).a) { + filter[`-a-${key}`] = (apply.exn.a as any).a[key]; } const matchingCreds = await holderClient.credentials().list({ filter }); @@ -385,6 +385,18 @@ test('single signature credentials', { timeout: 90000 }, async () => { offerSaid = offer.exn.d; assert.strictEqual(offer.exn.p, applySaid); + assert( + 'e' in offer.exn && + offer.exn.e && + typeof offer.exn.e === 'object' && + 'acdc' in offer.exn.e && + offer.exn.e.acdc && + typeof offer.exn.e.acdc === 'object' && + 'a' in offer.exn.e.acdc && + offer.exn.e.acdc.a && + typeof offer.exn.e.acdc.a === 'object' && + 'LEI' in offer.exn.e.acdc.a + ); assert.strictEqual(offer.exn.e.acdc.a.LEI, '5493001KJTIIGC8Y1R17'); await markAndRemoveNotification(verifierClient, verifierOfferNote); diff --git a/test-integration/multisig-holder.test.ts b/test-integration/multisig-holder.test.ts index 964e621b..37b7fd18 100644 --- a/test-integration/multisig-holder.test.ts +++ b/test-integration/multisig-holder.test.ts @@ -388,9 +388,14 @@ test('multisig', async function run() { exnRes.exn.i, recp ); - console.log( - `Member1 admitted credential with SAID : ${exnRes.exn.e.acdc.d}` - ); + + if (exnRes.exn && 'e' in exnRes.exn && (exnRes.exn as any).e?.acdc?.d) { + console.log( + `Member1 admitted credential with SAID : ${(exnRes.exn as any).e.acdc.d}` + ); + } else { + throw new Error('Expected property "e.acdc.d" not found on exnRes.exn'); + } const grantMsgSaid2 = await waitAndMarkNotification( client2, @@ -414,9 +419,13 @@ test('multisig', async function run() { exnRes.exn.i, recp2 ); - console.log( - `Member2 admitted credential with SAID : ${exnRes.exn.e.acdc.d}` - ); + if (exnRes.exn && 'e' in exnRes.exn && (exnRes.exn as any).e?.acdc?.d) { + console.log( + `Member2 admitted credential with SAID : ${(exnRes.exn as any).e.acdc.d}` + ); + } else { + throw new Error('Expected property "e.acdc.d" not found on exnRes.exn'); + } await waitOperation(client1, op1); await waitOperation(client2, op2); diff --git a/test-integration/multisig-join.test.ts b/test-integration/multisig-join.test.ts index 7f6d65e1..ce33eda4 100644 --- a/test-integration/multisig-join.test.ts +++ b/test-integration/multisig-join.test.ts @@ -96,7 +96,13 @@ describe('multisig-join', () => { const msgSaid = await waitAndMarkNotification(client2, '/multisig/icp'); const response = await client2.groups().getRequest(msgSaid); - const icp = response[0].exn.e.icp; + const exn = response[0].exn; + if (!('e' in exn) || !exn.e || !('icp' in exn.e) || !exn.e.icp) { + throw new Error( + 'exn.e.icp is missing from the group inception response' + ); + } + const icp = exn.e.icp; const icpResult2 = await client2.identifiers().create(nameMultisig, { algo: signify.Algos.group, @@ -346,13 +352,28 @@ describe('multisig-join', () => { .getRequest(rotationNotification3); const exn3 = response[0].exn; + if (!('e' in exn3) || !exn3.e || !('rot' in exn3.e) || !exn3.e.rot) { + throw new Error( + 'exn3.e.rot is missing from the group rotation response' + ); + } + if (!exn3.a || typeof (exn3.a as any).gid !== 'string') { + throw new Error('exn3.a.gid is missing or not a string'); + } const serder3 = new Serder(exn3.e.rot); const keeper3 = await client3.manager!.get(aid3); const sigs3 = keeper3.sign(signify.b(serder3.raw)); const joinOperation = await client3 .groups() - .join(nameMultisig, serder3, sigs3, exn3.a.gid, smids, rmids); + .join( + nameMultisig, + serder3, + sigs3, + (exn3.a as { gid: string }).gid, + smids, + rmids + ); await waitOperation(client3, joinOperation); diff --git a/test-integration/multisig-vlei-issuance.test.ts b/test-integration/multisig-vlei-issuance.test.ts index 2e2a89b2..3eb8d718 100644 --- a/test-integration/multisig-vlei-issuance.test.ts +++ b/test-integration/multisig-vlei-issuance.test.ts @@ -999,6 +999,7 @@ test('multisig-vlei-issuance', async function run() { throw new Error('Expected leCredbyQAR1.sad.a to be defined'); } + assert.equal(leCredbyQAR1.sad.a.i, aidLE.prefix); assert.equal(leCredbyQAR1.status.s, '0'); assert(leCredbyQAR1.atc !== undefined); const leCred = leCredbyQAR1; @@ -1253,6 +1254,7 @@ test('multisig-vlei-issuance', async function run() { throw new Error('Expected ecrCredbyLAR1.sad.a to be defined'); } + assert.equal(ecrCredbyLAR1.sad.a.i, aidECR.prefix); assert.equal(ecrCredbyLAR1.status.s, '0'); assert(ecrCredbyLAR1.atc !== undefined); const ecrCred = ecrCredbyLAR1; diff --git a/test-integration/multisig.test.ts b/test-integration/multisig.test.ts index 6e680e5e..f0bc34b3 100644 --- a/test-integration/multisig.test.ts +++ b/test-integration/multisig.test.ts @@ -178,7 +178,12 @@ test('multisig', async function run() { let res = await client2.groups().getRequest(msgSaid); let exn = res[0].exn; - let icp = exn.e.icp; + if (!('e' in exn) || !exn.e || !('icp' in exn.e) || !exn.e.icp) { + throw new Error( + 'exn.e.icp is missing from the group inception request' + ); + } + let icp = exn.e.icp as MultisigInceptEmbeds['icp']; let icpResult2 = await client2.identifiers().create('multisig', { algo: Algos.group, @@ -201,7 +206,7 @@ test('multisig', async function run() { icp: [serder, atc], }; - smids = exn.a.smids; + smids = (exn.a! as { smids: string[] }).smids; recp = [aid1['state'], aid3['state']].map((state) => state['i']); await client2 @@ -223,7 +228,12 @@ test('multisig', async function run() { res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - icp = exn.e.icp; + if (!('e' in exn) || !exn.e || !('icp' in exn.e) || !exn.e.icp) { + throw new Error( + 'exn.e.icp is missing from the group inception request' + ); + } + icp = exn.e.icp as MultisigInceptEmbeds['icp']; let icpResult3 = await client3.identifiers().create('multisig', { algo: Algos.group, mhab: aid3, @@ -245,7 +255,13 @@ test('multisig', async function run() { icp: [serder, atc], }; - smids = exn.a.smids; + smids = + exn.a && + typeof exn.a === 'object' && + exn.a !== null && + Array.isArray((exn.a as any).smids) + ? (exn.a as { smids: string[] }).smids + : []; recp = [aid1['state'], aid2['state']].map((state) => state['i']); await client3 @@ -368,12 +384,28 @@ test('multisig', async function run() { res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; // stamp, eid and role are provided in the exn message - let rpystamp = exn.e.rpy.dt; - let rpyrole = exn.e.rpy.a.role; - let rpyeid = exn.e.rpy.a.eid; - endRoleRes = await client2 - .identifiers() - .addEndRole('multisig', rpyrole, rpyeid, rpystamp); + if ( + 'e' in exn && + exn.e && + 'rpy' in exn.e && + exn.e.rpy && + typeof exn.e.rpy === 'object' && + exn.e.rpy !== null && + 'a' in (exn.e.rpy as Record) && + (exn.e.rpy as any).a + ) { + type RpyA = { role: string; eid: string }; + const rpyObj = exn.e.rpy as { a?: unknown; dt?: string }; + const rpyA = rpyObj.a as RpyA; + let rpystamp = rpyObj.dt; + let rpyrole = rpyA.role; + let rpyeid = rpyA.eid; + endRoleRes = await client2 + .identifiers() + .addEndRole('multisig', rpyrole, rpyeid, rpystamp); + } else { + throw new Error('Expected properties "e.rpy.a" not found on exn'); + } op2 = await endRoleRes.op(); rpy = endRoleRes.serder; sigs = endRoleRes.sigs; @@ -413,13 +445,28 @@ test('multisig', async function run() { ); res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - rpystamp = exn.e.rpy.dt; - rpyrole = exn.e.rpy.a.role; - rpyeid = exn.e.rpy.a.eid; - endRoleRes = await client3 - .identifiers() - .addEndRole('multisig', rpyrole, rpyeid, rpystamp); - + if ( + 'e' in exn && + exn.e && + 'rpy' in exn.e && + exn.e.rpy && + typeof exn.e.rpy === 'object' && + exn.e.rpy !== null && + 'a' in (exn.e.rpy as Record) && + (exn.e.rpy as any).a + ) { + type RpyA = { role: string; eid: string }; + const rpyObj = exn.e.rpy as { a?: unknown; dt?: string }; + const rpyA = rpyObj.a as RpyA; + const rpystamp = rpyObj.dt; + const rpyrole = rpyA.role; + const rpyeid = rpyA.eid; + endRoleRes = await client3 + .identifiers() + .addEndRole('multisig', rpyrole, rpyeid, rpystamp); + } else { + throw new Error('Expected properties "e.rpy.a" not found on exn'); + } op3 = await endRoleRes.op(); rpy = endRoleRes.serder; sigs = endRoleRes.sigs; @@ -508,8 +555,20 @@ test('multisig', async function run() { ); res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; - let ixn = exn.e.ixn; - data = ixn.a; + if ( + !('e' in exn) || + !exn.e || + !('ixn' in exn.e) || + !exn.e.ixn || + typeof exn.e.ixn !== 'object' || + (exn.e.ixn as any).a == null + ) { + throw new Error( + 'exn.e.ixn.a is missing from the group interaction event' + ); + } + let ixn1 = exn.e.ixn as { a: { i: string; s: string; d: string } }; + data = ixn1.a; icpResult2 = await client2.identifiers().interact('multisig', data); op2 = await icpResult2.op(); @@ -523,7 +582,10 @@ test('multisig', async function run() { ixn: [serder, atc], }; - smids = exn.a.smids; + if (!exn.a) { + throw new Error('exn.a is missing from the group interaction event'); + } + smids = (exn.a as { smids: string[] }).smids; recp = [aid1['state'], aid3['state']].map((state) => state['i']); await client2 @@ -546,8 +608,20 @@ test('multisig', async function run() { ); res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - ixn = exn.e.ixn; - data = ixn.a; + if ( + !('e' in exn) || + !exn.e || + !('ixn' in exn.e) || + !exn.e.ixn || + typeof exn.e.ixn !== 'object' || + (exn.e.ixn as any).a == null + ) { + throw new Error( + 'exn.e.ixn.a is missing from the group interaction event' + ); + } + let ixn2 = exn.e.ixn as { a: { i: string; s: string; d: string } }; + data = ixn2.a; icpResult3 = await client3.identifiers().interact('multisig', data); op3 = await icpResult3.op(); @@ -561,7 +635,7 @@ test('multisig', async function run() { ixn: [serder, atc], }; - smids = exn.a.smids; + smids = (exn.a as { smids: string[] }).smids; recp = [aid1['state'], aid2['state']].map((state) => state['i']); await client3 @@ -688,7 +762,10 @@ test('multisig', async function run() { rot: [serder, atc], }; - smids = exn.a.smids; + if (!exn.a) { + throw new Error('exn.a is missing from the group rotation event'); + } + smids = (exn.a as { smids: string[] }).smids; recp = [aid1State, aid3State].map((state) => state['i']); await client2 @@ -724,7 +801,7 @@ test('multisig', async function run() { rot: [serder, atc], }; - smids = exn.a.smids; + smids = (exn.a as { smids: string[] }).smids; recp = [aid1State, aid2State].map((state) => state['i']); await client3 @@ -912,8 +989,21 @@ test('multisig', async function run() { res = await client2.groups().getRequest(msgSaid); exn = res[0].exn; - const credentialSaid = exn.e.acdc.d; - const credRes2 = await client2.credentials().issue('multisig', exn.e.acdc); + if (!('e' in exn) || !exn.e || !('acdc' in exn.e) || !exn.e.acdc) { + throw new Error('exn.e.acdc is missing from the credential event'); + } + + const acdc = exn.e.acdc as CredentialData | undefined; + if (!acdc || !acdc.a) { + throw new Error('Credential subject (a) is missing'); + } + if (!acdc.d) { + throw new Error( + 'Credential SAID (acdc.d) is missing from the credential event' + ); + } + const credentialSaid: string = acdc.d; + const credRes2 = await client2.credentials().issue('multisig', acdc); op2 = credRes2.op; await multisigIssue(client2, 'member2', 'multisig', credRes2); @@ -927,7 +1017,12 @@ test('multisig', async function run() { res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - const credRes3 = await client3.credentials().issue('multisig', exn.e.acdc); + if (!('e' in exn) || !exn.e || !('acdc' in exn.e) || !exn.e.acdc) { + throw new Error('exn.e.acdc is missing from the credential event'); + } + const credRes3 = await client3 + .credentials() + .issue('multisig', exn.e.acdc as CredentialData); op3 = credRes3.op; await multisigIssue(client3, 'member3', 'multisig', credRes3); @@ -1084,12 +1179,13 @@ test('multisig', async function run() { msgSaid = await waitAndMarkNotification(client4, '/exn/ipex/grant'); console.log('Holder received exchange message with the grant message'); - res = await client4.exchanges().get(msgSaid); + + const exchangeResource = await client4.exchanges().get(msgSaid); const [admit, asigs, aend] = await client4.ipex().admit({ senderName: 'holder', message: '', - grantSaid: res.exn.d, + grantSaid: exchangeResource.exn.d, recipient: m['prefix'], }); diff --git a/test-integration/utils/multisig-utils.ts b/test-integration/utils/multisig-utils.ts index ba76000e..c8920f93 100644 --- a/test-integration/utils/multisig-utils.ts +++ b/test-integration/utils/multisig-utils.ts @@ -8,6 +8,8 @@ import signify, { d, messagize, HabState, + Icp, + MultisigExnEmbeds, } from 'signify-ts'; import { getStates, waitAndMarkNotification } from './test-util.ts'; import assert from 'assert'; @@ -37,9 +39,16 @@ export async function acceptMultisigIncept( const res = await client2.groups().getRequest(msgSaid); const exn = res[0].exn; - const icp = exn.e.icp; - const smids = exn.a.smids; - const rmids = exn.a.rmids; + + if (!('e' in exn) || !exn.e || !('icp' in exn.e) || !exn.e.icp) { + throw new Error( + 'exn.e.icp is missing from the group inception request' + ); + } + + const icp = exn.e.icp as Icp; + const smids = (exn.a as { smids: string[] }).smids; + const rmids = (exn.a as { rmids: string[] }).rmids; const states = await getStates(client2, smids); const rstates = await getStates(client2, rmids); @@ -299,7 +308,11 @@ export async function delegateMultisig( ); const res = await client.groups().getRequest(msgSaid); const exn = res[0].exn; - const ixn = exn.e.ixn; + if (!('e' in exn) || !exn.e) { + throw new Error('exn.e is missing from the group request'); + } + const embeds = exn.e as MultisigExnEmbeds; + const ixn = (embeds as any).ixn; anchor = ixn.a[0]; } From 4355327b2f7147feb983fac83eae072f28fdcaf3 Mon Sep 17 00:00:00 2001 From: Patrick Vu Date: Mon, 10 Nov 2025 09:31:23 +0700 Subject: [PATCH 5/7] fix conflicts + resolve review comments --- src/keri/app/aiding.ts | 4 +- src/keri/core/eventing.ts | 8 +-- src/keri/core/keyState.ts | 7 ++- src/types/keria-api-schema.ts | 4 +- test-integration/credentials.test.ts | 10 ++- test-integration/delegation-multisig.test.ts | 8 +-- test-integration/multisig-holder.test.ts | 33 +++++++--- test-integration/multisig-inception.test.ts | 8 +-- test-integration/multisig-join.test.ts | 20 +++--- test-integration/multisig.test.ts | 65 ++++++++++---------- test-integration/singlesig-drt.test.ts | 10 ++- test-integration/utils/multisig-utils.ts | 22 ++++--- test/app/aiding.test.ts | 4 +- 13 files changed, 116 insertions(+), 87 deletions(-) diff --git a/src/keri/app/aiding.ts b/src/keri/app/aiding.ts index 63865e93..1e4cef5b 100644 --- a/src/keri/app/aiding.ts +++ b/src/keri/app/aiding.ts @@ -13,8 +13,8 @@ import { components } from '../../types/keria-api-schema.ts'; /** Arguments required to create an identfier */ export interface CreateIdentiferArgs { transferable?: boolean; - isith?: string | number | string[]; - nsith?: string | number | string[]; + isith?: string | string[] | string[][]; + nsith?: string | string[] | string[][]; wits?: string[]; toad?: number; proxy?: string; diff --git a/src/keri/core/eventing.ts b/src/keri/core/eventing.ts index 6d14101d..f8763c5f 100644 --- a/src/keri/core/eventing.ts +++ b/src/keri/core/eventing.ts @@ -270,9 +270,9 @@ export function ample(n: number, f?: number, weak = true) { export interface InceptArgs { keys: Array; - isith?: number | string | Array; + isith?: string | string[] | string[][]; ndigs?: Array; - nsith?: number | string | Array; + nsith?: string | string[] | string[][]; toad?: number | string; wits?: Array; cnfg?: Array; @@ -304,7 +304,7 @@ export function incept({ const sner = new CesrNumber({}, 0); if (isith == undefined) { - isith = Math.max(1, Math.ceil(keys.length / 2)); + isith = Math.max(1, Math.ceil(keys.length / 2)).toString(); } const tholder = new Tholder({ sith: isith }); @@ -320,7 +320,7 @@ export function incept({ } if (nsith == undefined) { - nsith = Math.max(0, Math.ceil(ndigs.length / 2)); + nsith = Math.max(0, Math.ceil(ndigs.length / 2)).toString(); } const ntholder = new Tholder({ sith: nsith }); diff --git a/src/keri/core/keyState.ts b/src/keri/core/keyState.ts index 19745e7a..cbade1e4 100644 --- a/src/keri/core/keyState.ts +++ b/src/keri/core/keyState.ts @@ -44,7 +44,8 @@ export type Ixn = export type ExnV1 = | components['schemas']['EXN_V_1'] | components['schemas']['EXN_V_2']; -export type MultisigExnEmbeds = components['schemas']['MultisigExnEmbeds']; -export type MultisigInceptEmbeds = - components['schemas']['MultisigInceptEmbeds']; +export type Dip = + | components['schemas']['DIP_V_1'] + | components['schemas']['DIP_V_2']; +export type ExnEmbeds = components['schemas']['ExnEmbeds']; export type MultisigRpyEmbeds = components['schemas']['MultisigRpyEmbeds']; diff --git a/src/types/keria-api-schema.ts b/src/types/keria-api-schema.ts index 5638d920..1754edb4 100644 --- a/src/types/keria-api-schema.ts +++ b/src/types/keria-api-schema.ts @@ -96,7 +96,7 @@ export interface components { i: string; s: string; p: string; - a: components['schemas']['Seal'][]; + a: unknown; }; IXN_V_2: { v: string; @@ -105,7 +105,7 @@ export interface components { i: string; s: string; p: string; - a: components['schemas']['Seal'][]; + a: unknown; }; ICP_V_1: { v: string; diff --git a/test-integration/credentials.test.ts b/test-integration/credentials.test.ts index 63367ff5..cd66bd52 100644 --- a/test-integration/credentials.test.ts +++ b/test-integration/credentials.test.ts @@ -344,9 +344,13 @@ test('single signature credentials', { timeout: 90000 }, async () => { const apply = await holderClient.exchanges().get(holderApplyNote.a.d); applySaid = apply.exn.d; - const filter: { [x: string]: any } = { '-s': (apply.exn.a as any).s }; - for (const key in (apply.exn.a as any).a) { - filter[`-a-${key}`] = (apply.exn.a as any).a[key]; + const exnA = apply.exn.a as Record; + const filter: { [x: string]: unknown } = { '-s': exnA.s }; + const attributes = exnA.a as Record; + if (typeof attributes === 'object' && attributes !== null) { + for (const key in attributes) { + filter[`-a-${key}`] = attributes[key]; + } } const matchingCreds = await holderClient.credentials().list({ filter }); diff --git a/test-integration/delegation-multisig.test.ts b/test-integration/delegation-multisig.test.ts index 553466cd..5298b351 100644 --- a/test-integration/delegation-multisig.test.ts +++ b/test-integration/delegation-multisig.test.ts @@ -105,8 +105,8 @@ test('delegation-multisig', async () => { groupName: delegatorGroupName, localMemberName: delegator1Aid.name, participants: [delegator1Aid.prefix, delegator2Aid.prefix], - isith: 2, - nsith: 2, + isith: '2', + nsith: '2', toad: 2, wits: [ 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha', @@ -226,8 +226,8 @@ test('delegation-multisig', async () => { groupName: delegateeGroupName, localMemberName: delegatee1Aid.name, participants: [delegatee1Aid.prefix, delegatee2Aid.prefix], - isith: 2, - nsith: 2, + isith: '2', + nsith: '2', toad: 2, delpre: torpre, wits: [ diff --git a/test-integration/multisig-holder.test.ts b/test-integration/multisig-holder.test.ts index 37b7fd18..96914ed7 100644 --- a/test-integration/multisig-holder.test.ts +++ b/test-integration/multisig-holder.test.ts @@ -80,8 +80,8 @@ test('multisig', async function run() { op1 = await startMultisigIncept(client1, { groupName: 'holder', localMemberName: aid1.name, - isith: 2, - nsith: 2, + isith: '2', + nsith: '2', toad: aid1.state.b.length, wits: aid1.state.b, participants: [aid1.prefix, aid2.prefix], @@ -389,9 +389,17 @@ test('multisig', async function run() { recp ); - if (exnRes.exn && 'e' in exnRes.exn && (exnRes.exn as any).e?.acdc?.d) { + const exn1 = exnRes.exn; + if (!('e' in exn1) || !exn1.e) { + throw new Error('exn1.e is missing from the exchange result'); + } + + const acdcEmbeds1 = exn1.e as { acdc: { d: string } }; + const credentialSaid1 = acdcEmbeds1.acdc?.d; + + if (credentialSaid1) { console.log( - `Member1 admitted credential with SAID : ${(exnRes.exn as any).e.acdc.d}` + `Member1 admitted credential with SAID : ${credentialSaid1}` ); } else { throw new Error('Expected property "e.acdc.d" not found on exnRes.exn'); @@ -419,12 +427,23 @@ test('multisig', async function run() { exnRes.exn.i, recp2 ); - if (exnRes.exn && 'e' in exnRes.exn && (exnRes.exn as any).e?.acdc?.d) { + + const exn2 = exnRes.exn; + if (!('e' in exn2) || !exn2.e) { + throw new Error('exn2.e is missing from the exchange result'); + } + + const acdcEmbeds = exn2.e as { acdc: { d: string } }; + const credentialSaid = acdcEmbeds.acdc?.d; + + if (credentialSaid) { console.log( - `Member2 admitted credential with SAID : ${(exnRes.exn as any).e.acdc.d}` + `Member2 admitted credential with SAID : ${credentialSaid}` ); } else { - throw new Error('Expected property "e.acdc.d" not found on exnRes.exn'); + throw new Error( + 'Expected property "e.acdc.d" not found on exnRes2.exn' + ); } await waitOperation(client1, op1); diff --git a/test-integration/multisig-inception.test.ts b/test-integration/multisig-inception.test.ts index fa05144a..04d86a8f 100644 --- a/test-integration/multisig-inception.test.ts +++ b/test-integration/multisig-inception.test.ts @@ -42,8 +42,8 @@ test('multisig inception', async () => { localMemberName: 'member1', participants: [aid1, aid2], toad: 2, - isith: 2, - nsith: 2, + isith: '2', + nsith: '2', wits: [ 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha', 'BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM', @@ -97,8 +97,8 @@ test('multisig inception', async () => { localMemberName: 'member1', participants: [aid1, aid2], toad: 0, - isith: 2, - nsith: 2, + isith: '2', + nsith: '2', wits: [], }); console.log( diff --git a/test-integration/multisig-join.test.ts b/test-integration/multisig-join.test.ts index ce33eda4..35901688 100644 --- a/test-integration/multisig-join.test.ts +++ b/test-integration/multisig-join.test.ts @@ -1,4 +1,4 @@ -import signify, { KeyState, Serder, SignifyClient } from 'signify-ts'; +import signify, { KeyState, Serder, SignifyClient, Icp } from 'signify-ts'; import { getOrCreateClient, getOrCreateIdentifier, @@ -57,8 +57,8 @@ describe('multisig-join', () => { const icpResult = await client1.identifiers().create(nameMultisig, { algo: signify.Algos.group, mhab: aid1, - isith: 1, - nsith: 1, + isith: '1', + nsith: '1', toad: aid1.state.b.length, wits: aid1.state.b, states: states, @@ -102,7 +102,7 @@ describe('multisig-join', () => { 'exn.e.icp is missing from the group inception response' ); } - const icp = exn.e.icp; + const icp = exn.e.icp as Icp; const icpResult2 = await client2.identifiers().create(nameMultisig, { algo: signify.Algos.group, @@ -357,23 +357,17 @@ describe('multisig-join', () => { 'exn3.e.rot is missing from the group rotation response' ); } - if (!exn3.a || typeof (exn3.a as any).gid !== 'string') { + if (!exn3.a || typeof (exn3.a as { gid?: string }).gid !== 'string') { throw new Error('exn3.a.gid is missing or not a string'); } const serder3 = new Serder(exn3.e.rot); const keeper3 = await client3.manager!.get(aid3); const sigs3 = keeper3.sign(signify.b(serder3.raw)); + const exnA = exn3.a as { gid: string }; const joinOperation = await client3 .groups() - .join( - nameMultisig, - serder3, - sigs3, - (exn3.a as { gid: string }).gid, - smids, - rmids - ); + .join(nameMultisig, serder3, sigs3, exnA.gid, smids, rmids); await waitOperation(client3, joinOperation); diff --git a/test-integration/multisig.test.ts b/test-integration/multisig.test.ts index f0bc34b3..4b9a8ec8 100644 --- a/test-integration/multisig.test.ts +++ b/test-integration/multisig.test.ts @@ -10,6 +10,9 @@ import { messagize, b, d, + Icp, + CredentialData, + ExnEmbeds, } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env.ts'; import { @@ -136,8 +139,8 @@ test('multisig', async function run() { let icpResult1 = await client1.identifiers().create('multisig', { algo: Algos.group, mhab: aid1, - isith: 3, - nsith: 3, + isith: '3', + nsith: '3', toad: aid1.state.b.length, wits: aid1.state.b, states: states, @@ -183,7 +186,7 @@ test('multisig', async function run() { 'exn.e.icp is missing from the group inception request' ); } - let icp = exn.e.icp as MultisigInceptEmbeds['icp']; + let icp = exn.e.icp as Icp; let icpResult2 = await client2.identifiers().create('multisig', { algo: Algos.group, @@ -233,7 +236,7 @@ test('multisig', async function run() { 'exn.e.icp is missing from the group inception request' ); } - icp = exn.e.icp as MultisigInceptEmbeds['icp']; + icp = exn.e.icp as Icp; let icpResult3 = await client3.identifiers().create('multisig', { algo: Algos.group, mhab: aid3, @@ -259,7 +262,8 @@ test('multisig', async function run() { exn.a && typeof exn.a === 'object' && exn.a !== null && - Array.isArray((exn.a as any).smids) + 'smids' in exn.a && + Array.isArray((exn.a as { smids: unknown }).smids) ? (exn.a as { smids: string[] }).smids : []; recp = [aid1['state'], aid2['state']].map((state) => state['i']); @@ -392,7 +396,7 @@ test('multisig', async function run() { typeof exn.e.rpy === 'object' && exn.e.rpy !== null && 'a' in (exn.e.rpy as Record) && - (exn.e.rpy as any).a + (exn.e.rpy as { a?: unknown }).a ) { type RpyA = { role: string; eid: string }; const rpyObj = exn.e.rpy as { a?: unknown; dt?: string }; @@ -453,7 +457,7 @@ test('multisig', async function run() { typeof exn.e.rpy === 'object' && exn.e.rpy !== null && 'a' in (exn.e.rpy as Record) && - (exn.e.rpy as any).a + (exn.e.rpy as { a?: unknown }).a ) { type RpyA = { role: string; eid: string }; const rpyObj = exn.e.rpy as { a?: unknown; dt?: string }; @@ -554,20 +558,18 @@ test('multisig', async function run() { 'Member2 received exchange message to join the interaction event' ); res = await client2.groups().getRequest(msgSaid); + exn = res[0].exn; - if ( - !('e' in exn) || - !exn.e || - !('ixn' in exn.e) || - !exn.e.ixn || - typeof exn.e.ixn !== 'object' || - (exn.e.ixn as any).a == null - ) { - throw new Error( - 'exn.e.ixn.a is missing from the group interaction event' - ); + if (!('e' in exn) || !exn.e) { + throw new Error('exn.e is missing from the group request'); } - let ixn1 = exn.e.ixn as { a: { i: string; s: string; d: string } }; + + let embeds1 = exn.e as ExnEmbeds; + if (!('ixn' in embeds1) || !embeds1.ixn) { + throw new Error('ixn is missing from embeds'); + } + + let ixn1 = embeds1.ixn as { a: { i: string; s: string; d: string } }; data = ixn1.a; icpResult2 = await client2.identifiers().interact('multisig', data); @@ -606,22 +608,21 @@ test('multisig', async function run() { console.log( 'Member3 received exchange message to join the interaction event' ); + res = await client3.groups().getRequest(msgSaid); + exn = res[0].exn; - if ( - !('e' in exn) || - !exn.e || - !('ixn' in exn.e) || - !exn.e.ixn || - typeof exn.e.ixn !== 'object' || - (exn.e.ixn as any).a == null - ) { - throw new Error( - 'exn.e.ixn.a is missing from the group interaction event' - ); + if (!('e' in exn) || !exn.e) { + throw new Error('exn.e is missing from the group request'); + } + + const embeds2 = exn.e as ExnEmbeds; + if (!('ixn' in embeds2) || !embeds2.ixn) { + throw new Error('ixn is missing from embeds'); } - let ixn2 = exn.e.ixn as { a: { i: string; s: string; d: string } }; - data = ixn2.a; + + const ixn = embeds2.ixn; + data = ixn.a as { i: string; s: string; d: string }; icpResult3 = await client3.identifiers().interact('multisig', data); op3 = await icpResult3.op(); diff --git a/test-integration/singlesig-drt.test.ts b/test-integration/singlesig-drt.test.ts index d8e3cab1..38c95c40 100644 --- a/test-integration/singlesig-drt.test.ts +++ b/test-integration/singlesig-drt.test.ts @@ -1,5 +1,9 @@ import { afterAll, assert, beforeAll, describe, test } from 'vitest'; -import { CreateIdentiferArgs, SignifyClient } from 'signify-ts'; +import { + CreateIdentiferArgs, + RotateIdentifierArgs, + SignifyClient, +} from 'signify-ts'; import { assertOperations, getOrCreateClients, @@ -53,8 +57,8 @@ describe('singlesig-drt', () => { waitOperation(delegate, op2), ]); - kargs = {}; - result = await delegate.identifiers().rotate('delegate1', kargs); + let rotate_kargs = {} as RotateIdentifierArgs; + result = await delegate.identifiers().rotate('delegate1', rotate_kargs); op = await result.op(); assert.equal(op.name, `delegation.${result.serder.sad.d}`); diff --git a/test-integration/utils/multisig-utils.ts b/test-integration/utils/multisig-utils.ts index c8920f93..2e12348c 100644 --- a/test-integration/utils/multisig-utils.ts +++ b/test-integration/utils/multisig-utils.ts @@ -8,8 +8,8 @@ import signify, { d, messagize, HabState, - Icp, - MultisigExnEmbeds, + Dip, + ExnEmbeds, } from 'signify-ts'; import { getStates, waitAndMarkNotification } from './test-util.ts'; import assert from 'assert'; @@ -24,8 +24,8 @@ export interface StartMultisigInceptArgs { groupName: string; localMemberName: string; participants: string[]; - isith?: number | string | string[]; - nsith?: number | string | string[]; + isith?: string | string[] | string[][]; + nsith?: string | string[] | string[][]; toad?: number; wits?: string[]; delpre?: string; @@ -46,7 +46,7 @@ export async function acceptMultisigIncept( ); } - const icp = exn.e.icp as Icp; + const icp = exn.e.icp as Dip; const smids = (exn.a as { smids: string[] }).smids; const rmids = (exn.a as { rmids: string[] }).rmids; const states = await getStates(client2, smids); @@ -306,14 +306,20 @@ export async function delegateMultisig( console.log( `${aid.name}(${aid.prefix}) received exchange message to join the interaction event` ); + const res = await client.groups().getRequest(msgSaid); const exn = res[0].exn; if (!('e' in exn) || !exn.e) { throw new Error('exn.e is missing from the group request'); } - const embeds = exn.e as MultisigExnEmbeds; - const ixn = (embeds as any).ixn; - anchor = ixn.a[0]; + + const embeds = exn.e as ExnEmbeds; + if (!('ixn' in embeds) || !embeds.ixn) { + throw new Error('ixn is missing from embeds'); + } + + const ixn = embeds.ixn; + anchor = (ixn.a as Array<{ i: string; s: string; d: string }>)[0]; } // const {delResult, delOp} = await retry(async () => { diff --git a/test/app/aiding.test.ts b/test/app/aiding.test.ts index bcc7527b..fbc8b206 100644 --- a/test/app/aiding.test.ts +++ b/test/app/aiding.test.ts @@ -476,8 +476,8 @@ describe('Aiding', () => { it('CreateIdentiferArgs', () => { let args: CreateIdentiferArgs; args = { - isith: 1, - nsith: 1, + isith: '1', + nsith: '1', }; args = { isith: '1', From 2f8f5ccd983b8a9049dbcabbf559c2545809e523 Mon Sep 17 00:00:00 2001 From: Patrick Vu Date: Tue, 11 Nov 2025 18:02:45 +0700 Subject: [PATCH 6/7] fix types for multisig --- src/keri/app/aiding.ts | 4 ++-- src/keri/core/eventing.ts | 4 ++-- src/types/keria-api-schema.ts | 5 ++--- test-integration/delegation-multisig.test.ts | 8 ++++---- test-integration/multisig-holder.test.ts | 4 ++-- test-integration/multisig-inception.test.ts | 8 ++++---- test-integration/multisig-join.test.ts | 4 ++-- test-integration/multisig.test.ts | 4 ++-- test-integration/utils/multisig-utils.ts | 4 ++-- 9 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/keri/app/aiding.ts b/src/keri/app/aiding.ts index 1e4cef5b..5c6ebd95 100644 --- a/src/keri/app/aiding.ts +++ b/src/keri/app/aiding.ts @@ -13,8 +13,8 @@ import { components } from '../../types/keria-api-schema.ts'; /** Arguments required to create an identfier */ export interface CreateIdentiferArgs { transferable?: boolean; - isith?: string | string[] | string[][]; - nsith?: string | string[] | string[][]; + isith?: string | number | string[] | string[][]; + nsith?: string | number | string[] | string[][]; wits?: string[]; toad?: number; proxy?: string; diff --git a/src/keri/core/eventing.ts b/src/keri/core/eventing.ts index f8763c5f..66dbbb47 100644 --- a/src/keri/core/eventing.ts +++ b/src/keri/core/eventing.ts @@ -270,9 +270,9 @@ export function ample(n: number, f?: number, weak = true) { export interface InceptArgs { keys: Array; - isith?: string | string[] | string[][]; + isith?: string | number | string[] | string[][]; ndigs?: Array; - nsith?: string | string[] | string[][]; + nsith?: string | number | string[] | string[][]; toad?: number | string; wits?: Array; cnfg?: Array; diff --git a/src/types/keria-api-schema.ts b/src/types/keria-api-schema.ts index 1754edb4..c00af223 100644 --- a/src/types/keria-api-schema.ts +++ b/src/types/keria-api-schema.ts @@ -635,10 +635,9 @@ export interface components { }; MultisigInceptEmbeds: { icp: components['schemas']['Icp']; - rot?: components['schemas']['Rot']; }; MultisigRotateEmbeds: { - rot: unknown; + rot: components['schemas']['Rot']; }; MultisigInteractEmbeds: { ixn: components['schemas']['Ixn']; @@ -721,13 +720,13 @@ export interface components { paths: { [key: string]: unknown; }; - e: components['schemas']['ExnEmbeds']; /** @default null */ groupName: string | null; /** @default null */ memberName: string | null; /** @default null */ sender: string | null; + e?: components['schemas']['ExnEmbeds']; }; }; responses: never; diff --git a/test-integration/delegation-multisig.test.ts b/test-integration/delegation-multisig.test.ts index 5298b351..553466cd 100644 --- a/test-integration/delegation-multisig.test.ts +++ b/test-integration/delegation-multisig.test.ts @@ -105,8 +105,8 @@ test('delegation-multisig', async () => { groupName: delegatorGroupName, localMemberName: delegator1Aid.name, participants: [delegator1Aid.prefix, delegator2Aid.prefix], - isith: '2', - nsith: '2', + isith: 2, + nsith: 2, toad: 2, wits: [ 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha', @@ -226,8 +226,8 @@ test('delegation-multisig', async () => { groupName: delegateeGroupName, localMemberName: delegatee1Aid.name, participants: [delegatee1Aid.prefix, delegatee2Aid.prefix], - isith: '2', - nsith: '2', + isith: 2, + nsith: 2, toad: 2, delpre: torpre, wits: [ diff --git a/test-integration/multisig-holder.test.ts b/test-integration/multisig-holder.test.ts index 96914ed7..c34d4345 100644 --- a/test-integration/multisig-holder.test.ts +++ b/test-integration/multisig-holder.test.ts @@ -80,8 +80,8 @@ test('multisig', async function run() { op1 = await startMultisigIncept(client1, { groupName: 'holder', localMemberName: aid1.name, - isith: '2', - nsith: '2', + isith: 2, + nsith: 2, toad: aid1.state.b.length, wits: aid1.state.b, participants: [aid1.prefix, aid2.prefix], diff --git a/test-integration/multisig-inception.test.ts b/test-integration/multisig-inception.test.ts index 04d86a8f..fa05144a 100644 --- a/test-integration/multisig-inception.test.ts +++ b/test-integration/multisig-inception.test.ts @@ -42,8 +42,8 @@ test('multisig inception', async () => { localMemberName: 'member1', participants: [aid1, aid2], toad: 2, - isith: '2', - nsith: '2', + isith: 2, + nsith: 2, wits: [ 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha', 'BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM', @@ -97,8 +97,8 @@ test('multisig inception', async () => { localMemberName: 'member1', participants: [aid1, aid2], toad: 0, - isith: '2', - nsith: '2', + isith: 2, + nsith: 2, wits: [], }); console.log( diff --git a/test-integration/multisig-join.test.ts b/test-integration/multisig-join.test.ts index 35901688..25483da8 100644 --- a/test-integration/multisig-join.test.ts +++ b/test-integration/multisig-join.test.ts @@ -57,8 +57,8 @@ describe('multisig-join', () => { const icpResult = await client1.identifiers().create(nameMultisig, { algo: signify.Algos.group, mhab: aid1, - isith: '1', - nsith: '1', + isith: 1, + nsith: 1, toad: aid1.state.b.length, wits: aid1.state.b, states: states, diff --git a/test-integration/multisig.test.ts b/test-integration/multisig.test.ts index 4b9a8ec8..7ce147f6 100644 --- a/test-integration/multisig.test.ts +++ b/test-integration/multisig.test.ts @@ -139,8 +139,8 @@ test('multisig', async function run() { let icpResult1 = await client1.identifiers().create('multisig', { algo: Algos.group, mhab: aid1, - isith: '3', - nsith: '3', + isith: 3, + nsith: 3, toad: aid1.state.b.length, wits: aid1.state.b, states: states, diff --git a/test-integration/utils/multisig-utils.ts b/test-integration/utils/multisig-utils.ts index 2e12348c..da323394 100644 --- a/test-integration/utils/multisig-utils.ts +++ b/test-integration/utils/multisig-utils.ts @@ -24,8 +24,8 @@ export interface StartMultisigInceptArgs { groupName: string; localMemberName: string; participants: string[]; - isith?: string | string[] | string[][]; - nsith?: string | string[] | string[][]; + isith?: string | number | string[] | string[][]; + nsith?: string | number | string[] | string[][]; toad?: number; wits?: string[]; delpre?: string; From a9d1ad56707dced2a321a19ac6eac35c7e04aae6 Mon Sep 17 00:00:00 2001 From: Patrick Vu Date: Wed, 12 Nov 2025 15:06:22 +0700 Subject: [PATCH 7/7] continue fixing Multisig types and update KERIA API schema --- src/keri/app/aiding.ts | 2 +- src/keri/core/eventing.ts | 4 ++-- src/types/keria-api-schema.ts | 14 +++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/keri/app/aiding.ts b/src/keri/app/aiding.ts index 5c6ebd95..62793d34 100644 --- a/src/keri/app/aiding.ts +++ b/src/keri/app/aiding.ts @@ -183,7 +183,7 @@ export class Identifier { if (!transferable) { ncount = 0; - nsith = '0'; + nsith = 0; dcode = MtrDex.Ed25519N; } diff --git a/src/keri/core/eventing.ts b/src/keri/core/eventing.ts index 66dbbb47..194b5247 100644 --- a/src/keri/core/eventing.ts +++ b/src/keri/core/eventing.ts @@ -304,7 +304,7 @@ export function incept({ const sner = new CesrNumber({}, 0); if (isith == undefined) { - isith = Math.max(1, Math.ceil(keys.length / 2)).toString(); + isith = Math.max(1, Math.ceil(keys.length / 2)); } const tholder = new Tholder({ sith: isith }); @@ -320,7 +320,7 @@ export function incept({ } if (nsith == undefined) { - nsith = Math.max(0, Math.ceil(ndigs.length / 2)).toString(); + nsith = Math.max(0, Math.ceil(ndigs.length / 2)); } const ntholder = new Tholder({ sith: nsith }); diff --git a/src/types/keria-api-schema.ts b/src/types/keria-api-schema.ts index c00af223..45a4137e 100644 --- a/src/types/keria-api-schema.ts +++ b/src/types/keria-api-schema.ts @@ -694,11 +694,20 @@ export interface components { }; MultisigRevokeEmbeds: { rev: components['schemas']['REV_V_1']; - anc: unknown; + anc: + | components['schemas']['IXN_V_1'] + | components['schemas']['IXN_V_2'] + | components['schemas']['ICP_V_1'] + | components['schemas']['ICP_V_2'] + | components['schemas']['ROT_V_1'] + | components['schemas']['ROT_V_2'] + | components['schemas']['DIP_V_1'] + | components['schemas']['DIP_V_2'] + | components['schemas']['DRT_V_1'] + | components['schemas']['DRT_V_2']; }; MultisigRpyEmbeds: { rpy: components['schemas']['Rpy']; - anc: unknown; }; MultisigExnEmbeds: { exn: components['schemas']['Exn']; @@ -726,7 +735,6 @@ export interface components { memberName: string | null; /** @default null */ sender: string | null; - e?: components['schemas']['ExnEmbeds']; }; }; responses: never;