import b58 from 'bs58check'; const HDNode = require('bip32'); export class MultisigCosigner { constructor(data) { this._data = data; this._fp = false; this._xpub = false; this._path = false; this._valid = false; this._cosigners = []; // is it plain simple Zpub/Ypub/xpub? if (data.startsWith('Zpub') && MultisigCosigner.isXpubValid(data)) { this._fp = '00000000'; this._xpub = data; this._path = "m/48'/0'/0'/2'"; this._valid = true; this._cosigners = [true]; return; } else if (data.startsWith('Ypub') && MultisigCosigner.isXpubValid(data)) { this._fp = '00000000'; this._xpub = data; this._path = "m/48'/0'/0'/1'"; this._valid = true; this._cosigners = [true]; return; } else if (data.startsWith('xpub') && MultisigCosigner.isXpubValid(data)) { this._fp = '00000000'; this._xpub = data; this._path = "m/45'"; this._valid = true; this._cosigners = [true]; return; } // is it wallet descriptor? if (data.startsWith('[')) { const end = data.indexOf(']'); const part = data.substr(1, end - 1).replace(/[h]/g, "'"); this._fp = part.split('/')[0]; const xpub = data.substr(end + 1); if (MultisigCosigner.isXpubValid(xpub)) { this._xpub = xpub; this._path = 'm'; for (let c = 0; c < part.split('/').length; c++) { if (c === 0) continue; this._path += '/' + part.split('/')[c]; } this._cosigners = [true]; this._valid = true; return; } } // is it cobo json? try { const json = JSON.parse(data); if (json.xfp && json.xpub && json.path) { this._fp = json.xfp; this._xpub = json.xpub; this._path = json.path; this._cosigners = [true]; this._valid = true; return; } } catch (_) { this._valid = false; } // is it coldcard json? try { const json = JSON.parse(data); if (json.p2sh && json.p2sh_deriv && json.xfp) { const cc = new MultisigCosigner(MultisigCosigner.exportToJson(json.xfp, json.p2sh, json.p2sh_deriv)); this._valid = true; this._cosigners.push(cc); } if (json.p2wsh_p2sh && json.p2wsh_p2sh_deriv && json.xfp) { const cc = new MultisigCosigner(MultisigCosigner.exportToJson(json.xfp, json.p2wsh_p2sh, json.p2wsh_p2sh_deriv)); this._valid = true; this._cosigners.push(cc); } if (json.p2wsh && json.p2wsh_deriv && json.xfp) { const cc = new MultisigCosigner(MultisigCosigner.exportToJson(json.xfp, json.p2wsh, json.p2wsh_deriv)); this._valid = true; this._cosigners.push(cc); } } catch (_) { this._valid = false; } } static _zpubToXpub(zpub) { let data = b58.decode(zpub); data = data.slice(4); data = Buffer.concat([Buffer.from('0488b21e', 'hex'), data]); return b58.encode(data); } static isXpubValid(key) { let xpub; try { xpub = MultisigCosigner._zpubToXpub(key); HDNode.fromBase58(xpub); return true; } catch (_) {} return false; } static exportToJson(xfp, xpub, path) { return JSON.stringify({ xfp: xfp, xpub: xpub, path: path, }); } isValid() { return this._valid; } getFp() { return this._fp; } getXpub() { return this._xpub; } getPath() { return this._path; } howManyCosignersWeHave() { return this._cosigners.length; } /** * * @returns {Array.} */ getAllCosigners() { return this._cosigners; } }