UNPKG

@gleif-it/vlei-verifier-workflows

Version:

Workflows for vLEI users and vLEI credentials for the vLEI-verifier service

816 lines (815 loc) 46.1 kB
import { strict as assert } from 'assert'; import { boolean } from 'mathjs'; import SignifyClient from 'signify-ts'; import { resolveOobi, waitOperation, getOrCreateAID, getOrCreateClients, getOrCreateContact, createTimestamp, getIssuedCredential, getReceivedCredential, sendGrantMessage, sendAdmitMessage, getOrIssueCredential, getOrCreateRegistry, waitForCredential, admitSinglesig, waitAndMarkNotification, assertOperations, sleep, revokeCredential, } from './utils/test-util.js'; import { addEndRoleMultisig, admitMultisig, createAIDMultisig, createRegistryMultisig, delegateMultisig, grantMultisig, issueCredentialMultisig, multisigRevoke, } from './utils/multisig-utils.js'; import { retry } from './utils/retry.js'; import { QVI_SCHEMA_URL, LE_SCHEMA_URL, ECR_AUTH_SCHEMA_URL, ECR_SCHEMA_URL, OOR_AUTH_SCHEMA_URL, OOR_SCHEMA_URL, CRED_RETRY_DEFAULTS, } from './constants.js'; import { buildTestData } from './utils/generate-test-data.js'; import { WorkflowState } from './workflow-state.js'; export const VleiIssuance = { // Create client for given AID createClient: async (secret, agentName) => { const workflow_state = WorkflowState.getInstance(); console.log(`Creating client for secret: ${secret}`); const client = await getOrCreateClients(1, [secret], false); workflow_state.clients.set(agentName, client[0]); return true; }, // Create AID createAid: async (identifierData) => { console.log('Creating AID'); const workflow_state = WorkflowState.getInstance(); let aid; if (identifierData.type == 'singlesig') { workflow_state.aidsInfo.set(identifierData.name, identifierData); aid = await VleiIssuance.createAidSinglesig(identifierData); await VleiIssuance.fetchOobi(identifierData); await VleiIssuance.createContacts(identifierData); await VleiIssuance.resolveOobi(identifierData); workflow_state.aids.set(identifierData.name, aid); } else { workflow_state.aidsInfo.set(identifierData.name, identifierData); aid = await VleiIssuance.createAidMultisig(identifierData); await VleiIssuance.fetchOobi(identifierData); workflow_state.aids.set(identifierData.name, aid); } }, // Fetch OOBIs for each client! fetchOobis: async () => { console.log('Fetching OOBIs'); const workflow_state = WorkflowState.getInstance(); for (const [, aidInfo] of workflow_state.aidsInfo) { await VleiIssuance.fetchOobi(aidInfo); } }, fetchOobi: async (identifierData) => { let client; let oobi; const workflow_state = WorkflowState.getInstance(); if (identifierData.type === 'singlesig') { const singlesigIdentifierData = identifierData; client = workflow_state.clients.get(singlesigIdentifierData.agent.name); oobi = await client.oobis().get(identifierData.name, 'agent'); if (oobi) { workflow_state.oobis.set(singlesigIdentifierData.name, [oobi]); } } else { const multisigIdentifierData = identifierData; const oobis = []; for (const _ of multisigIdentifierData.identifiers) { const identifier = workflow_state.aidsInfo.get(multisigIdentifierData.identifiers[0]); client = workflow_state.clients.get(identifier.agent.name); oobi = await client.oobis().get(identifier.name, 'agent'); oobis.push(oobi); } if (oobi) { workflow_state.oobis.set(identifierData.name, oobis); } } }, // Create contacts between clients createContacts: async (identifierData) => { console.log('Creating Contacts'); const workflow_state = WorkflowState.getInstance(); for (const [, contactIdentifierData] of workflow_state.aidsInfo) { if (contactIdentifierData.type === 'multisig' || identifierData.type === 'multisig') continue; if (identifierData.name != contactIdentifierData.name) { await VleiIssuance.createContact(identifierData, contactIdentifierData); } } }, createContact: async (aidInfoA, aidInfoB) => { const workflow_state = WorkflowState.getInstance(); if (aidInfoA.type == 'singlesig') { const singlesigIdentifierDataA = aidInfoA; const singlesigIdentifierDataB = aidInfoB; const clientA = workflow_state.clients.get(singlesigIdentifierDataA.agent.name); const clientB = workflow_state.clients.get(singlesigIdentifierDataB.agent.name); const oobiA = workflow_state.oobis.get(singlesigIdentifierDataA.name)?.[0] .oobis[0]; const oobiB = workflow_state.oobis.get(singlesigIdentifierDataB.name)?.[0] .oobis[0]; await getOrCreateContact(clientA, singlesigIdentifierDataB.name, oobiB); await getOrCreateContact(clientB, singlesigIdentifierDataA.name, oobiA); } }, // Resolve OOBIs for each client! and schema resolveOobis: () => { const schemaUrls = [ QVI_SCHEMA_URL, LE_SCHEMA_URL, ECR_AUTH_SCHEMA_URL, ECR_SCHEMA_URL, OOR_AUTH_SCHEMA_URL, OOR_SCHEMA_URL, ]; console.log('Resolving OOBIs'); const workflow_state = WorkflowState.getInstance(); for (const [, client] of workflow_state.clients) { schemaUrls.forEach(async (schemaUrl) => { await resolveOobi(client, schemaUrl); }); } }, resolveOobi: (identifierData) => { const schemaUrls = [ QVI_SCHEMA_URL, LE_SCHEMA_URL, ECR_AUTH_SCHEMA_URL, ECR_SCHEMA_URL, OOR_AUTH_SCHEMA_URL, OOR_SCHEMA_URL, ]; if (identifierData.type === 'singlesig') { console.log('Resolving OOBIs for client'); const workflow_state = WorkflowState.getInstance(); const singlesigIdentifierData = identifierData; const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); schemaUrls.forEach(async (schemaUrl) => { await resolveOobi(client, schemaUrl); }); } }, createRegistry: async (identifierData) => { console.log('Creating Registries'); const workflow_state = WorkflowState.getInstance(); let registry; if (identifierData.type == 'multisig') { registry = await VleiIssuance.createRegistryMultisig(identifierData); } else { const singlesigIdentifierData = identifierData; const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); registry = await getOrCreateRegistry(client, workflow_state.aids.get(identifierData.name), `${identifierData.name}Registry`); } workflow_state.registries.set(identifierData.name, registry); }, createAidSinglesig: async (identifierData) => { const workflow_state = WorkflowState.getInstance(); const delegator = identifierData.delegator; const kargsSinglesigAID = { toad: workflow_state.kargsAID.toad, wits: workflow_state.kargsAID.wits, }; const singlesigIdentifierData = identifierData; const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); if (delegator != null) { try { const aid = await client.identifiers().get(identifierData.name); return aid; } catch { console.log(`Creating delegated AID for: ${identifierData.name}`); } kargsSinglesigAID.delpre = workflow_state.aids.get(delegator).prefix; const delegatorIdentifierData = workflow_state.aidsInfo.get(delegator); const delegatorclient = workflow_state.clients.get(delegatorIdentifierData.agent.name); const delegatorAid = workflow_state.aids.get(delegator); // Resolve delegator's oobi const oobi1 = await delegatorclient.oobis().get(delegator, 'agent'); await resolveOobi(client, oobi1.oobis[0], delegator); // Delegate client! creates delegate AID const icpResult2 = await client .identifiers() .create(identifierData.name, { delpre: delegatorAid.prefix }); const op2 = await icpResult2.op(); const delegateAidPrefix = op2.name.split('.')[1]; console.log("Delegate's prefix:", delegateAidPrefix); // client! 1 approves delegation const anchor = { i: delegateAidPrefix, s: '0', d: delegateAidPrefix, }; const result = await retry(async () => { const apprDelRes = await delegatorclient .delegations() .approve(delegator, anchor); await waitOperation(delegatorclient, await apprDelRes.op()); console.log('Delegator approve delegation submitted'); return apprDelRes; }); assert.equal(JSON.stringify(result.serder.ked.a[0]), JSON.stringify(anchor)); const op3 = await client.keyStates().query(delegatorAid.prefix, '1'); await waitOperation(client, op3); // Delegate client! checks approval await waitOperation(client, op2); const aid2 = await client.identifiers().get(identifierData.name); assert.equal(aid2.prefix, delegateAidPrefix); console.log('Delegation approved for aid:', aid2.prefix); await assertOperations(delegatorclient, client); const rpyResult2 = await client .identifiers() .addEndRole(identifierData.name, 'agent', client.agent.pre); await waitOperation(client, await rpyResult2.op()); return aid2; } else { const aid = await getOrCreateAID(client, identifierData.name, kargsSinglesigAID); return aid; } }, createAidMultisig: async (identifierData) => { const workflow_state = WorkflowState.getInstance(); const multisigIdentifierData = identifierData; let multisigAids = []; const aidIdentifierNames = multisigIdentifierData.identifiers; const issuerAids = aidIdentifierNames.map((aidIdentifierName) => workflow_state.aids.get(aidIdentifierName)) || []; try { for (const aidIdentifierName of aidIdentifierNames) { const singlesigIdentifierData = workflow_state.aidsInfo.get(aidIdentifierName); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); multisigAids.push(await client.identifiers().get(identifierData.name)); } const multisigAid = multisigAids[0]; console.log(`${identifierData.name} AID: ${multisigAid.prefix}`); return multisigAid; } catch { multisigAids = []; } if (multisigAids.length == 0) { const rstates = issuerAids.map((aid) => aid.state); const states = rstates; const kargsMultisigAID = { algo: SignifyClient.Algos.group, isith: multisigIdentifierData.isith, nsith: multisigIdentifierData.nsith, toad: workflow_state.kargsAID.toad, wits: workflow_state.kargsAID.wits, states: states, rstates: rstates, }; if (identifierData.delegator != null) { kargsMultisigAID.delpre = workflow_state.aids.get(multisigIdentifierData.delegator).prefix; } const multisigOps = []; for (let index = 0; index < issuerAids.length; index++) { const aid = issuerAids[index]; const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); const kargsMultisigAIDClone = { ...kargsMultisigAID, mhab: aid }; const otherAids = issuerAids.filter((aidTmp) => aid !== aidTmp); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); const op = await createAIDMultisig(client, aid, otherAids, identifierData.name, kargsMultisigAIDClone, index === 0 // Set true for the first operation ); multisigOps.push([client, op]); } if (multisigIdentifierData.delegator) { // Approve delegation const delegatoridentifierData = workflow_state.aidsInfo.get(multisigIdentifierData.delegator); const delegatorAidIdentifierNames = delegatoridentifierData.identifiers; const delegatorAids = delegatorAidIdentifierNames.map((aidIdentifierName) => workflow_state.aids.get(aidIdentifierName)) || []; const teepre = multisigOps[0][1].name.split('.')[1]; const anchor = { i: teepre, s: '0', d: teepre, }; let delegatorClientInitiator; const delegatorMultisigAid = workflow_state.aids.get(delegatoridentifierData.name); const delegateOps = []; for (let index = 0; index < delegatoridentifierData.identifiers.length; index++) { const curAidName = delegatoridentifierData.identifiers[index]; const curidentifierData = workflow_state.aidsInfo.get(curAidName); const aid = workflow_state.aids.get(curAidName); const otherAids = delegatorAids.filter((aidTmp) => aid.prefix !== aidTmp.prefix); const delegatorclient = workflow_state.clients.get(curidentifierData.agent.name); const delApprOp = await delegateMultisig(delegatorclient, aid, otherAids, delegatorMultisigAid, anchor, index === 0); delegateOps.push([delegatorclient, delApprOp]); if (index === 0) { delegatorClientInitiator = delegatorclient; } else { await waitAndMarkNotification(delegatorClientInitiator, '/multisig/ixn'); } } for (const [client, op] of delegateOps) { await waitOperation(client, op); } for (const identifier of delegatoridentifierData.identifiers) { const curidentifierData = workflow_state.aidsInfo.get(identifier); const delegatorclient = workflow_state.clients.get(curidentifierData.agent.name); const queryOp1 = await delegatorclient .keyStates() .query(delegatorMultisigAid.prefix, '1'); await waitOperation(delegatorclient, queryOp1); } for (const identifier of multisigIdentifierData.identifiers) { const curidentifierData = workflow_state.aidsInfo.get(identifier); const delegateeclient = workflow_state.clients.get(curidentifierData.agent.name); const ksteetor1 = await delegateeclient .keyStates() .query(delegatorMultisigAid.prefix, '1'); await waitOperation(delegateeclient, ksteetor1); } } // Wait for all multisig operations to complete for (const [client, op] of multisigOps) { await waitOperation(client, op); } // Wait for multisig inception notifications for all clients const tmpAidData = workflow_state.aidsInfo.get(issuerAids[0].name); await waitAndMarkNotification(workflow_state.clients.get(tmpAidData.agent.name), '/multisig/icp'); // Retrieve the newly created AIDs for all clients multisigAids = await Promise.all(issuerAids.map(async (aid) => { const tmpAidData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(tmpAidData.agent.name); return await client.identifiers().get(identifierData.name); })); assert(multisigAids.every((aid) => aid.prefix === multisigAids[0].prefix)); assert(multisigAids.every((aid) => aid.name === multisigAids[0].name)); const multisigAid = multisigAids[0]; // Skip if they have already been authorized. let oobis = await Promise.all(issuerAids.map(async (aid) => { const tmpAidData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(tmpAidData.agent.name); return await client.oobis().get(multisigAid.name, 'agent'); })); if (oobis.some((oobi) => oobi.oobis.length == 0)) { const timestamp = createTimestamp(); // Add endpoint role for all clients const roleOps = await Promise.all(issuerAids.map(async (aid, index) => { const otherAids = issuerAids.filter((_, i) => i !== index); const tmpAidData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(tmpAidData.agent.name); return await addEndRoleMultisig(client, multisigAid.name, aid, otherAids, multisigAid, timestamp, index === 0); })); // Wait for all role operations to complete for each client! for (const [i, roleOpGroup] of roleOps.entries()) { for (const roleOp of roleOpGroup) { const tmpAidData = workflow_state.aidsInfo.get(issuerAids[i].name); const client = workflow_state.clients.get(tmpAidData.agent.name); await waitOperation(client, roleOp); } } // Wait for role resolution notifications for all clients // await waitAndMarkNotification(workflow_state.clients.get(workflow_state.aidsInfo.get(issuerAids[0].name).agent.name), "/multisig/rpy"); await Promise.all(issuerAids.map((aid) => { const tmpAidData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(tmpAidData.agent.name); return waitAndMarkNotification(client, '/multisig/rpy'); })); // Retrieve the OOBI again after the operation for all clients oobis = await Promise.all(issuerAids.map(async (aid) => { const tmpAidData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(tmpAidData.agent.name); return await client.oobis().get(multisigAid.name, 'agent'); })); } // Ensure that all OOBIs are consistent across all clients assert(oobis.every((oobi) => oobi.role === oobis[0].role)); assert(oobis.every((oobi) => oobi.oobis[0] === oobis[0].oobis[0])); const oobi = oobis[0].oobis[0].split('/agent/')[0]; const clients = Array.from(workflow_state.clients.values()).flat(); await Promise.all(clients.map(async (client) => await getOrCreateContact(client, multisigAid.name, oobi))); console.log(`${identifierData.name} AID: ${multisigAid.prefix}`); return multisigAid; } }, createRegistryMultisig: async (identifierData) => { const multisigIdentifierData = identifierData; const workflow_state = WorkflowState.getInstance(); const multisigAid = workflow_state.aids.get(identifierData.name); const registryIdentifierName = `${identifierData.name}Registry`; const aidIdentifierNames = multisigIdentifierData.identifiers; const registries = new Array(); const issuerAids = aidIdentifierNames.map((aidIdentifierName) => workflow_state.aids.get(aidIdentifierName)) || []; // Check if the registries already exist for (const aidIdentifierName of aidIdentifierNames) { const singlesigIdentifierData = workflow_state.aidsInfo.get(aidIdentifierName); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); let tmpRegistry = await client.registries().list(multisigAid.name); tmpRegistry = tmpRegistry.filter((reg) => reg.name == `${identifierData.name}Registry`); registries.push(tmpRegistry); } // Check if registries exist const allEmpty = registries.every((registry) => registry.length === 0); if (allEmpty) { const nonce = SignifyClient.randomNonce(); const registryOps = issuerAids.map((aid, index) => { const tmpAidData = workflow_state.aidsInfo.get(aid.name); const otherAids = issuerAids.filter((_, i) => i !== index); const client = workflow_state.clients.get(tmpAidData.agent.name); return createRegistryMultisig(client, aid, otherAids, multisigAid, registryIdentifierName, nonce, index === 0 // Use true for the first operation, false for others ); }); // Await all registry creation operations const createdOps = await Promise.all(registryOps); // Wait for all operations to complete across multiple clients await Promise.all(createdOps.map(async (op, index) => { const tmpAidData = workflow_state.aidsInfo.get(issuerAids[index].name); const client = workflow_state.clients.get(tmpAidData.agent.name); return await waitOperation(client, op); })); // Wait for multisig inception notification for each client! const tmpAidData = workflow_state.aidsInfo.get(issuerAids[0].name); await waitAndMarkNotification(workflow_state.clients.get(tmpAidData.agent.name), '/multisig/vcp'); // Recheck the registries for each client! const updatedRegistries = await Promise.all(issuerAids.map((aid) => { const tmpAidData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(tmpAidData.agent.name); return client.registries().list(multisigAid.name); })); // Update the `registries` array with the new values registries.splice(0, registries.length, ...updatedRegistries); // Ensure that all registries match the first one const firstRegistry = registries[0][0]; registries.forEach((registry) => { assert.equal(registry[0].regk, firstRegistry.regk); assert.equal(registry[0].name, firstRegistry.name); }); // Save the first registry and return it workflow_state.registries.set(multisigAid.name, firstRegistry); console.log(`${multisigAid.name} Registry created`); return firstRegistry; } else { return registries[0][0]; } }, getOrIssueCredential: async (credId, credName, attributes, issuerAidKey, issueeAidKey, credSourceId, generateTestData = false, testName = 'default_test') => WorkflowState.getInstance().aidsInfo.get(issuerAidKey).type === 'multisig' ? await VleiIssuance.getOrIssueCredentialMultiSig(credId, credName, attributes, issuerAidKey, issueeAidKey, credSourceId, generateTestData, testName) : await VleiIssuance.getOrIssueCredentialSingleSig(credId, credName, attributes, issuerAidKey, issueeAidKey, credSourceId, generateTestData, testName), revokeCredential: async (credId, issuerAidKey, issueeAidKey, generateTestData = false, testName = 'default_test') => { const workflow_state = WorkflowState.getInstance(); const issuerAidInfo = workflow_state.aidsInfo.get(issuerAidKey); if (issuerAidInfo.type === 'multisig') { return await VleiIssuance.revokeCredentialMultiSig(credId, issuerAidKey, issueeAidKey, generateTestData, testName); } else { return await VleiIssuance.revokeCredentialSingleSig(credId, issuerAidKey, issueeAidKey, generateTestData, testName); } }, getOrIssueCredentialSingleSig: async (credId, credName, attributes, issuerAidKey, issueeAidKey, credSourceId, generateTestData = false, testName = 'default_test') => { const workflow_state = WorkflowState.getInstance(); const credInfo = workflow_state.credentialsInfo.get(credName); const issuerAID = workflow_state.aids.get(issuerAidKey); const recipientAID = workflow_state.aids.get(issueeAidKey); const issuerAIDInfo = workflow_state.aidsInfo.get(issuerAidKey); const recipientAIDInfo = workflow_state.aidsInfo.get(issueeAidKey); const issuerclient = workflow_state.clients.get(issuerAIDInfo.agent.name); const recipientclient = workflow_state.clients.get(recipientAIDInfo.agent.name); const issuerRegistry = workflow_state.registries.get(issuerAIDInfo.name); const schema = workflow_state.schemas[credInfo.schema]; const rules = workflow_state.rules[credInfo.rules]; const privacy = credInfo.privacy; let credSource = null; if (credSourceId != null) { const credType = credInfo.credSource['type']; const issuerCred = workflow_state.credentials.get(credSourceId); const credO = credInfo.credSource['o'] || null; credSource = VleiIssuance.buildCredSource(credType, issuerCred, credO); } if (attributes['AID'] != null) { attributes.AID = workflow_state.aids.get(attributes['AID']).prefix; } const credData = { ...credInfo.attributes, ...attributes }; const cred = await getOrIssueCredential(issuerclient, issuerAID, recipientAID, issuerRegistry, credData, schema, rules || undefined, credSource || undefined, boolean(privacy || false)); let credHolder = await getReceivedCredential(recipientclient, cred.sad.d); if (!credHolder) { await sendGrantMessage(issuerclient, issuerAID, recipientAID, cred); await sendAdmitMessage(recipientclient, recipientAID, issuerAID); credHolder = await retry(async () => { const cCred = await getReceivedCredential(recipientclient, cred.sad.d); assert(cCred !== undefined); return cCred; }, CRED_RETRY_DEFAULTS); } assert.equal(credHolder.sad.d, cred.sad.d); assert.equal(credHolder.sad.s, schema); assert.equal(credHolder.sad.i, issuerAID.prefix); assert.equal(credHolder.sad.a.i, recipientAID.prefix); assert.equal(credHolder.status.s, '0'); assert(credHolder.atc !== undefined); workflow_state.credentials.set(credId, cred); const credCesr = await recipientclient.credentials().get(cred.sad.d, true); if (generateTestData) { const tmpCred = cred; const testData = { aid: recipientAID.prefix, lei: credData.LEI, credential: { raw: tmpCred, cesr: credCesr }, engagementContextRole: credData.engagementContextRole || credData.officialRole, }; await buildTestData(testData, testName, issueeAidKey); } const response = { roleClient: recipientclient, ecrAid: recipientAID, creds: { [credId]: { cred: cred, credCesr: credCesr } }, idAlias: issueeAidKey, }; return [response, credData.engagementContextRole]; }, getOrIssueCredentialMultiSig: async (credId, credName, attributes, issuerAidKey, issueeAidKey, credSourceId, _generateTestData = false, _testName = 'default_test') => { const workflow_state = WorkflowState.getInstance(); const credInfo = workflow_state.credentialsInfo.get(credName); const issuerAidInfo = workflow_state.aidsInfo.get(issuerAidKey); const recipientAidInfo = workflow_state.aidsInfo.get(issueeAidKey); const issuerAIDMultisig = workflow_state.aids.get(issuerAidKey); const recipientAID = workflow_state.aids.get(issueeAidKey); const schema = workflow_state.schemas[credInfo.schema]; let rules = workflow_state.rules[credInfo.rules]; const privacy = credInfo.privacy; const registryName = issuerAidInfo.name; const issuerRegistry = workflow_state.registries.get(registryName); const issuerAids = issuerAidInfo.identifiers.map((identifier) => workflow_state.aids.get(identifier)) || []; let recepientAids = []; if (recipientAidInfo.type === 'multisig') { const multisigIdentifierData = recipientAidInfo; recepientAids = multisigIdentifierData.identifiers.map((identifier) => workflow_state.aids.get(identifier)) || []; } else { recepientAids = [workflow_state.aids.get(recipientAidInfo.name)]; } let credSource = null; if (credSourceId != null) { const credType = credInfo.credSource['type']; const issuerCred = workflow_state.credentials.get(credSourceId); const credO = credInfo.credSource['o'] || null; credSource = VleiIssuance.buildCredSource(credType, issuerCred, credO); credSource = credSource ? { e: credSource } : undefined; } rules = rules ? { r: rules } : undefined; // Issuing a credential let creds = await Promise.all(issuerAids.map((aid) => { const identifierData = workflow_state.aidsInfo.get(aid.name); const singlesigData = identifierData; const client = workflow_state.clients.get(singlesigData.agent.name); return getIssuedCredential(client, issuerAIDMultisig, recipientAID, schema); })); if (creds.every((cred) => !cred)) { if (attributes['AID'] != null) { attributes.AID = workflow_state.aids.get(attributes['AID']).prefix; } const credData = { ...credInfo.attributes, ...attributes }; const kargsSub = { i: recipientAID.prefix, dt: createTimestamp(), u: privacy ? new SignifyClient.Salter({}).qb64 : undefined, ...credData, }; const kargsIss = { i: issuerAIDMultisig.prefix, ri: issuerRegistry.regk, s: schema, a: kargsSub, u: privacy ? new SignifyClient.Salter({}).qb64 : undefined, ...credSource, ...rules, }; const IssOps = await Promise.all(issuerAids.map((aid, index) => { const identifierData = workflow_state.aidsInfo.get(aid.name); const singlesigData = identifierData; const client = workflow_state.clients.get(singlesigData.agent.name); return issueCredentialMultisig(client, aid, issuerAids.filter((_, i) => i !== index), issuerAIDMultisig.name, kargsIss, index === 0); })); await Promise.all(issuerAids.map((aid, index) => { const singlesigData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigData.agent.name); return waitOperation(client, IssOps[index]); })); let tmpAidData = workflow_state.aidsInfo.get(issuerAids[0].name); await waitAndMarkNotification(workflow_state.clients.get(tmpAidData.agent.name), '/multisig/iss'); creds = await Promise.all(issuerAids.map((aid) => { const singlesigData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigData.agent.name); return getIssuedCredential(client, issuerAIDMultisig, recipientAID, schema); })); sleep(1000); const grantTime = createTimestamp(); await Promise.all(creds.map((cred, index) => { const singlesigData = workflow_state.aidsInfo.get(issuerAids[index].name); const client = workflow_state.clients.get(singlesigData.agent.name); return grantMultisig(client, issuerAids[index], issuerAids.filter((_, i) => i !== index), issuerAIDMultisig, recipientAID, cred, grantTime, index === 0); })); sleep(1000); tmpAidData = workflow_state.aidsInfo.get(issuerAids[0].name); await waitAndMarkNotification(workflow_state.clients.get(tmpAidData.agent.name), '/multisig/exn'); } const cred = creds[0]; // Exchange grant and admit messages. // Check if the recipient is a singlesig AID if (recipientAidInfo.type === 'multisig') { let credsReceived = await Promise.all(recepientAids.map((aid) => { const singlesigData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigData.agent.name); return getReceivedCredential(client, cred.sad.d); })); if (credsReceived.every((cred) => cred === undefined)) { const admitTime = createTimestamp(); await Promise.all(recepientAids.map((aid, index) => { const singlesigData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigData.agent.name); return admitMultisig(client, aid, recepientAids.filter((_, i) => i !== index), recipientAID, issuerAIDMultisig, admitTime); })); sleep(2000); for (const aid of issuerAids) { const singlesigData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigData.agent.name), '/exn/ipex/admit'); } for (const aid of recepientAids) { const singlesigData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigData.agent.name), '/multisig/exn'); } for (const aid of recepientAids) { const singlesigData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigData.agent.name), '/exn/ipex/admit'); } sleep(1000); credsReceived = await Promise.all(recepientAids.map((aid) => { const singlesigData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigData.agent.name); return waitForCredential(client, cred.sad.d); })); // Assert received credential details for (const credReceived of credsReceived) { assert.equal(cred.sad.d, credReceived.sad.d); } } } else { const singlesigData = workflow_state.aidsInfo.get(recepientAids[0].name); let credReceived = await getReceivedCredential(workflow_state.clients.get(singlesigData.agent.name), cred.sad.d); if (!credReceived) { await admitSinglesig(workflow_state.clients.get(singlesigData.agent.name), workflow_state.aids.get(recepientAids[0].name).name, issuerAIDMultisig); sleep(2000); for (const aid of issuerAids) { const singlesigData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigData.agent.name), '/exn/ipex/admit'); } credReceived = await waitForCredential(workflow_state.clients.get(singlesigData.agent.name), cred.sad.d); } assert.equal(cred.sad.d, credReceived.sad.d); } console.log(`${issuerAIDMultisig.name} has issued a ${recipientAID.name} vLEI credential with SAID:`, cred.sad.d); workflow_state.credentials.set(credId, cred); return [cred, null]; }, revokeCredentialSingleSig: async (credId, issuerAidKey, issueeAidKey, generateTestData = false, testName = 'default_test') => { const workflow_state = WorkflowState.getInstance(); const cred = workflow_state.credentials.get(credId); const issuerAID = workflow_state.aids.get(issuerAidKey); const recipientAID = workflow_state.aids.get(issueeAidKey); const issuerAIDInfo = workflow_state.aidsInfo.get(issuerAidKey); const recipientAIDInfo = workflow_state.aidsInfo.get(issueeAidKey); const recipientclient = workflow_state.clients.get(recipientAIDInfo.agent.name); const issuerclient = workflow_state.clients.get(issuerAIDInfo.agent.name); const revCred = await revokeCredential(issuerclient, issuerAID, cred.sad.d); workflow_state.credentials.set(credId, revCred); const credCesr = await issuerclient.credentials().get(revCred.sad.d, true); if (generateTestData) { const tmpCred = revCred; const testData = { aid: recipientAID.prefix, lei: revCred.sad.a.LEI, credential: { raw: tmpCred, cesr: credCesr }, engagementContextRole: revCred.sad.a.engagementContextRole || revCred.sad.a.officialRole, }; await buildTestData(testData, testName, issueeAidKey, 'revoked_'); } const response = { roleClient: recipientclient, ecrAid: recipientAID, creds: { credId: { cred: cred, credCesr: credCesr } }, idAlias: issueeAidKey, }; return [response, revCred.sad.a.engagementContextRole]; }, revokeCredentialMultiSig: async (credId, issuerAidKey, issueeAidKey, generateTestData = false, testName = 'default_test') => { const workflow_state = WorkflowState.getInstance(); const recipientAID = workflow_state.aids.get(issueeAidKey); const cred = workflow_state.credentials.get(credId); const issuerAidInfo = workflow_state.aidsInfo.get(issuerAidKey); const issuerAIDMultisig = workflow_state.aids.get(issuerAidKey); const issuerAids = issuerAidInfo.identifiers.map((identifier) => workflow_state.aids.get(identifier)) || []; let issuerclient; const revOps = []; let i = 0; const REVTIME = new Date().toISOString().replace('Z', '000+00:00'); for (const issuerAid of issuerAids) { const aidInfo = workflow_state.aidsInfo.get(issuerAid.name); issuerclient = workflow_state.clients.get(aidInfo.agent.name); if (i != 0) { const msgSaid = await waitAndMarkNotification(issuerclient, '/multisig/rev'); console.log(`Multisig AID ${issuerAid.name} received exchange message to join the credential revocation event`); await issuerclient.groups().getRequest(msgSaid); } const revResult = await issuerclient .credentials() .revoke(issuerAIDMultisig.name, cred.sad.d, REVTIME); revOps.push([issuerclient, revResult.op]); await multisigRevoke(issuerclient, issuerAid.name, issuerAIDMultisig.name, revResult.rev, revResult.anc); i += 1; } for (const [client, op] of revOps) { await waitOperation(client, op); } const revCred = await issuerclient.credentials().get(cred.sad.d); workflow_state.credentials.set(credId, revCred); if (generateTestData) { const tmpCred = revCred; const credCesr = await issuerclient .credentials() .get(revCred.sad.d, true); const testData = { aid: recipientAID.prefix, lei: revCred.sad.a.LEI, credential: { raw: tmpCred, cesr: credCesr }, engagementContextRole: revCred.sad.a.engagementContextRole || revCred.sad.a.officialRole, }; await buildTestData(testData, testName, issueeAidKey, 'revoked_'); } return [revCred, null]; }, notifyCredentialIssuee: async (credId, issuerAidKey, issueeAidKey) => { const workflow_state = WorkflowState.getInstance(); const cred = workflow_state.credentials.get(credId); if (!cred) { console.log(`notifyCredential: credential with credId=${credId} was not found`); throw new Error(`notifyCredential: credential with credId=${credId} was not found`); } const issuerAID = workflow_state.aids.get(issuerAidKey); const recipientAID = workflow_state.aids.get(issueeAidKey); const issuerAIDInfo = workflow_state.aidsInfo.get(issuerAidKey); const recipientAIDInfo = workflow_state.aidsInfo.get(issueeAidKey); if (issuerAIDInfo.type === 'multisig') { const multisigIdentifierData = issuerAIDInfo; const schema = workflow_state.schemas[cred.schema]; const issuerAids = multisigIdentifierData.identifiers.map((identifier) => workflow_state.aids.get(identifier)) || []; const creds = await Promise.all(issuerAids.map((aid) => { const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); return getIssuedCredential(client, issuerAID, recipientAID, schema); })); sleep(1000); const grantTime = createTimestamp(); await Promise.all(creds.map((cred, index) => { const singlesigIdentifierData = workflow_state.aidsInfo.get(issuerAids[index].name); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); return grantMultisig(client, issuerAids[index], issuerAids.filter((_, i) => i !== index), issuerAID, recipientAID, cred, grantTime, index === 0); })); } else { const singlesigIdentifierData = issuerAIDInfo; const issuerclient = workflow_state.clients.get(singlesigIdentifierData.agent.name); await sendGrantMessage(issuerclient, issuerAID, recipientAID, cred); } if (recipientAIDInfo.type === 'multisig') { const multisigRecipientIdentifierData = recipientAIDInfo; const multisigIssuerIdentifierData = issuerAIDInfo; const admitTime = createTimestamp(); const issuerAids = multisigIssuerIdentifierData.identifiers.map((identifier) => workflow_state.aids.get(identifier)) || []; const recepientAids = multisigRecipientIdentifierData.identifiers.map((identifier) => workflow_state.aids.get(identifier)) || []; await Promise.all(recepientAids.map((aid, index) => { const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); return admitMultisig(client, aid, recepientAids.filter((_, i) => i !== index), recipientAID, issuerAID, admitTime); })); sleep(2000); for (const aid of issuerAids) { const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigIdentifierData.agent.name), '/exn/ipex/admit'); } for (const aid of recepientAids) { const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigIdentifierData.agent.name), '/multisig/exn'); } for (const aid of recepientAids) { const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); await waitAndMarkNotification(workflow_state.clients.get(singlesigIdentifierData.agent.name), '/exn/ipex/admit'); } sleep(1000); await Promise.all(recepientAids.map((aid) => { const singlesigIdentifierData = workflow_state.aidsInfo.get(aid.name); const client = workflow_state.clients.get(singlesigIdentifierData.agent.name); return waitForCredential(client, cred.sad.d); })); } else { const singlesigIdentifierData = recipientAIDInfo; const recipientclient = workflow_state.clients.get(singlesigIdentifierData.agent.name); await sendAdmitMessage(recipientclient, recipientAID, issuerAID); } }, buildCredSource: (credType, cred, o) => { const credDict = { n: cred.sad.d, s: cred.sad.s, }; if (o != null) { credDict['o'] = o; } const credSource = SignifyClient.Saider.saidify({ d: '', [credType]: credDict, })[1]; return credSource; }, };