UNPKG

@bb-did/ion-tools

Version:

ION Tools - utilities to make working with the ION network and using ION DIDs easy peasy lemon squeezy

167 lines (149 loc) 5.39 kB
import { IonDid, IonRequest, LocalSigner } from '@bb-did/ion-sdk'; import { generateKeyPair } from './utils.js'; export class DID { #ops; #opQueue = Promise.resolve(); #longForm; #longFormPromise; #generateKeyPair; constructor(options = { }) { this.#ops = options.ops || [ ]; this.#generateKeyPair = options.generateKeyPair || generateKeyPair; if (!this.#ops.length) { this.#ops.push(this.generateOperation('create', options.content || { }, false)); } } async generateOperation(type, content, commit = true) { return this.#addToOpQueue(() => this.#generateOperation(type, content, commit)); } async #addToOpQueue(callback = () => Promise.resolve()) { const opQueue = this.#opQueue; this.#opQueue = new Promise((resolve, reject) => { opQueue.finally(() => callback().then(resolve, reject)); }); return this.#opQueue; } async #generateOperation(type, content, commit) { let lastOp = this.#ops[this.#ops.length - 1]; if (lastOp && lastOp.operation === 'deactivate') { throw 'Cannot perform further operations on a deactivated DID'; } let op = { operation: type, content }; if (type !== 'create') { op.previous = this.#ops.reduce((last, op) => { return op.operation === type || (op.operation === 'recover' && (type === 'deactivate' || type === 'update')) ? op : last; }, this.#ops[0]); } if (type === 'create' || type === 'recover') { op.recovery = await this.#generateKeyPair(); } if (type !== 'deactivate') { op.update = await this.#generateKeyPair(); } if (commit) { this.#ops.push(op); } return op; } async generateRequest(payload = 0, options = { }) { let op; if (typeof payload === 'number') { await this.#addToOpQueue(); op = await this.getOperation(payload); } else { op = payload; } switch (op.operation) { case 'update': console.log('==============######################====================') return IonRequest.createUpdateRequest({ didSuffix: await this.getSuffix(), signer: options.signer || LocalSigner.create(op.previous.update.privateJwk), updatePublicKey: op.previous.update.publicJwk, nextUpdatePublicKey: op.update.publicJwk, servicesToAdd: op.content?.addServices, idsOfServicesToRemove: op.content?.removeServices, publicKeysToAdd: op.content?.addPublicKeys, idsOfPublicKeysToRemove: op.content?.removePublicKeys }); case 'recover': return IonRequest.createRecoverRequest({ didSuffix: await this.getSuffix(), signer: options.signer || LocalSigner.create(op.previous.recovery.privateJwk), recoveryPublicKey: op.previous.recovery.publicJwk, nextRecoveryPublicKey: op.recovery.publicJwk, nextUpdatePublicKey: op.update.publicJwk, document: op.content }); case 'deactivate': return IonRequest.createDeactivateRequest({ didSuffix: await this.getSuffix(), recoveryPublicKey: op.previous.recovery.publicJwk, signer: options.signer || LocalSigner.create(op.previous.recovery.privateJwk) }); case 'create': default: return IonRequest.createCreateRequest({ recoveryKey: op.recovery.publicJwk, updateKey: op.update.publicJwk, document: op.content }); } } async getAllOperations() { return Promise.all(this.#ops); } async getOperation(index) { return this.#ops[index]; } async getState() { const [ shortForm, longForm, ops ] = await Promise.all([ this.getURI('short'), this.getURI(), this.getAllOperations() ]); return { shortForm, longForm, ops }; } /** * returns the suffix portion of the DID string for the DID URI the class instance represents * @example * <caption>example DID URI: `did:ion:EiCZws6U61LV3YmvxmOIlt4Ap5RSJdIkb_lJXhuUPqQYBg`</caption> * * // returns: EiCZws6U61LV3YmvxmOIlt4Ap5RSJdIkb_lJXhuUPqQYBg * did.getSuffix() * @returns {string} suffix */ async getSuffix() { const uri = await this.getURI('short'); return uri.split(':').pop(); } /** * returns either the long or short form URI for the DID based on the form provided * @param {'long' | 'short'} form - There are two forms of ION DID URI, the Long-Form URI, which can * be used instantly without anchoring an ION DID, and the Short-Form URI, which is only * resolvable after a DID has been published to the ION network. * @returns {Promise<string>} */ async getURI(form = 'long') { if (this.#longFormPromise) { await this.#longFormPromise; } if (!this.#longForm) { this.#longFormPromise = this.#addToOpQueue(async () => { const create = await this.getOperation(0); console.log(JSON.stringify(create)) return IonDid.createLongFormDid({ recoveryKey: create.recovery.publicJwk, updateKey: create.update.publicJwk, document: create.content }); }); this.#longForm = await this.#longFormPromise; this.#longFormPromise = undefined; } return !form || form === 'long' ? this.#longForm : this.#longForm.split(':').slice(0, -1).join(':'); } }