UNPKG

cosmic-lib

Version:

A JavaScript implementation of the CosmicLink protocol for Stellar

593 lines (491 loc) 17.3 kB
"use strict"; /** * Contains functions that probe the blockchain or federation servers to collect * datas. * * @exports resolve */ var _regeneratorRuntime = require("@babel/runtime/regenerator"); var _asyncToGenerator = require("@babel/runtime/helpers/asyncToGenerator"); var resolve = exports; var misc = require("@cosmic-plus/jsutils/es5/misc"); var StellarSdk = require("@cosmic-plus/base/es5/stellar-sdk"); var specs = require("./specs"); var status = require("./status"); /** * Returns the * [Server]{@link https://stellar.github.io/js-stellar-sdk/Server.html} object * for **horizon**, or for **network**, or for the current network. * * @param {string} [network] 'public', 'test' or a network passphrase * @param {string} [horizon] A horizon URL * @returns {Server} A StellarSdk Server object */ resolve.server = function (conf) { var network = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : conf.network; var horizon = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : conf.horizon; if (!horizon) horizon = resolve.horizon(conf, network); if (!horizon) throw new Error("No horizon node defined for selected network."); if (!conf.current.server[horizon]) { conf.current.server[horizon] = new StellarSdk.Server(horizon); } return conf.current.server[horizon]; }; /** * Switch to the current network, or to **network** if provided. * * @deprecated StellarSdk global `Network` setting is deprecated. * @param {string} [network] 'public', 'test' or a network passphrase * @returns {Server} A StellarSdk Server object */ resolve.useNetwork = function (conf) { var network = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : conf.network; // DEPRECATED - to be removed when in sync with stellar-sdk 3.x. console.warn("`.selectNetwork()`, `.useNetwork()`, as well as StellarSdk global `Network` setting are deprecated. Please use `cosmicLib.config.network` or pass parameter explicitely."); var passphrase = resolve.networkPassphrase(conf, network); var currentPassphrase = resolve.networkPassphrase(); if (passphrase !== currentPassphrase) { // eslint-disable-next-line no-console console.log("Switch to network: " + network); StellarSdk.Network.use(new StellarSdk.Network(passphrase)); } }; /** * Returns the curent Horizon node URL, or the Horizon node URL for **network** * if provided. * * @param {string} [network] A network name or passphrase. */ resolve.horizon = function (conf) { var network = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : conf.network; if (conf.horizon) { return conf.horizon; } else { var passphrase = resolve.networkPassphrase(conf, network); if (conf.current && conf.current.horizon[passphrase]) { return conf.current.horizon[passphrase]; } } }; /** * Returns the current network passphrase, or the passphrase for **network** is * provided. */ resolve.networkPassphrase = function () { var conf = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var network = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : conf.network; if (network === undefined) { // DEPRECATED: To be removed in sync with stellar-sdk 3.x. var currentNetwork = StellarSdk.Network.current(); if (currentNetwork) return currentNetwork.networkPassphrase(); } else { return conf.current.passphrase[network] || network; } }; /** * Returns the network name for **network passphrase**, or `undefined`. * * @param {string} networkPassphrase * @return {string} */ resolve.networkName = function () { var conf = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var networkPassphrase = arguments.length > 1 ? arguments[1] : undefined; var index = Object.values(conf.current.passphrase).indexOf(networkPassphrase); if (index === -1) return networkPassphrase;else return Object.keys(conf.current.passphrase)[index]; }; /** * Returns the federation server * [Account]{@link https://stellar.github.io/js-stellar-sdk/Account.html} * for **address**. * * @async * @param {string} address A Stellar public key or a federated address * @return {} Resolve to federation server response */ resolve.address = function (conf, address) { var cache = conf.cache; if (cache && cache.destination[address]) return cache.destination[address]; var promise = addressResolver(conf, address); if (cache) cache.destination[address] = promise; return promise; }; function addressResolver(_x, _x2) { return _addressResolver.apply(this, arguments); } /** * Returns the * [AccountResponse]{@link https://stellar.github.io/js-stellar-sdk/AccountResponse.html} * object for **address**. * * @param {string} address A public key or a federated address * @return {Object} The AccountResponse */ function _addressResolver() { _addressResolver = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(conf, address) { var account, accountId; return _regeneratorRuntime.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: _context5.prev = 0; _context5.next = 3; return StellarSdk.FederationServer.resolve(address); case 3: account = _context5.sent; accountId = account.account_id; if (accountId) { _context5.next = 7; break; } throw new Error("Unknow address"); case 7: if (!account.memo_type) delete account.memo; if (address !== accountId) account.address = address; if (conf.aliases && conf.aliases[accountId]) { account.alias = conf.aliases[accountId]; } return _context5.abrupt("return", account); case 13: _context5.prev = 13; _context5.t0 = _context5["catch"](0); console.error(_context5.t0); status.error(conf, "Can't resolve: " + misc.shorter(address)); status.fail(conf, "Unresolved address", "throw"); case 18: case "end": return _context5.stop(); } } }, _callee5, null, [[0, 13]]); })); return _addressResolver.apply(this, arguments); } resolve.account = /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(conf, address, quietFlag) { var account, accountId, cache, promise; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return resolve.address(conf, address); case 2: account = _context.sent; accountId = account.account_id; cache = conf.cache; if (!(cache && cache.account[accountId])) { _context.next = 7; break; } return _context.abrupt("return", cache.account[accountId]); case 7: promise = accountResolver(conf, accountId, quietFlag); if (cache) cache.account[accountId] = promise; return _context.abrupt("return", promise); case 10: case "end": return _context.stop(); } } }, _callee); })); return function (_x3, _x4, _x5) { return _ref.apply(this, arguments); }; }(); function accountResolver(_x6, _x7, _x8) { return _accountResolver.apply(this, arguments); } /** * Returns `true` if **address** account is empty, `false` otherwise. * * @async * @param {string} address Public key or federated address * @return {boolean} */ function _accountResolver() { _accountResolver = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(conf, accountId, quietFlag) { var server, accountResponse; return _regeneratorRuntime.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: server = resolve.server(conf); _context6.prev = 1; _context6.next = 4; return server.loadAccount(accountId); case 4: accountResponse = _context6.sent; return _context6.abrupt("return", accountResponse); case 8: _context6.prev = 8; _context6.t0 = _context6["catch"](1); if (!quietFlag) { _context6.next = 14; break; } throw _context6.t0; case 14: if (_context6.t0.response) { status.error(conf, "Empty account: " + misc.shorter(accountId), "throw"); } else { status.error(conf, "Invalid horizon node: " + resolve.horizon(conf), "throw"); } case 15: case "end": return _context6.stop(); } } }, _callee6, null, [[1, 8]]); })); return _accountResolver.apply(this, arguments); } resolve.isAccountEmpty = function (conf, address) { return resolve.account(conf, address, true).then(function () { return false; })["catch"](function () { return true; }); }; /** * Returns the account object for transaction source **address`** with sequence * set at **sequence** if provided. If **address** is not provided, returns the * neutral account object instead (as in SEP-0007 specifications). * * @param {string} [address] * @param {string|numbre} [sequence] * @return {AccountResponse} */ resolve.txSourceAccount = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(conf, address, sequence) { var destination, account, baseAccount; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: if (address) { _context2.next = 4; break; } return _context2.abrupt("return", makeAccountResponse(conf, specs.neutralAccountId, "-1")); case 4: _context2.next = 6; return resolve.address(conf, address); case 6: destination = _context2.sent; if (destination.memo) status.error(conf, "Invalid transaction source address (requires a memo)", "throw"); _context2.next = 10; return resolve.account(conf, destination.account_id); case 10: account = _context2.sent; if (sequence) { baseAccount = new StellarSdk.Account(account.id, sequence); baseAccount.sequence = baseAccount.sequence.sub(1); account._baseAccount = baseAccount; } return _context2.abrupt("return", account); case 13: case "end": return _context2.stop(); } } }, _callee2); })); return function (_x9, _x10, _x11) { return _ref2.apply(this, arguments); }; }(); /** * Creates an AccountResponse object with signers set for an empty account. * * @param {string} publicKey * @param {string} sequence [description] * @return {AccountResponse} */ function makeAccountResponse(conf, publicKey, sequence) { var account = new StellarSdk.Account(publicKey, sequence); if (conf.cache) conf.cache.account[publicKey] = account; account.id = publicKey; account.signers = [{ public_key: publicKey, weight: 1, key: publicKey, type: "ed25519_public_key" }]; return account; } /** * Returns the array of all source accounts ID involved in **transaction**. * * @param {Transaction} transaction * @return {Array} */ resolve.txSources = function (conf, transaction) { if (!transaction.source) throw new Error("No source for transaction"); var extra = resolve.extra(conf, transaction); if (extra.cache.txSources) return extra.cache.txSources; var array = extra.cache.txSources = [transaction.source]; var _loop = function _loop(index) { var source = transaction.operations[index].source; if (source && !array.find(function (a) { return a === source; })) array.push(source); }; for (var index in transaction.operations) { _loop(index); } return array; }; /** * Returns an object such as: * * ```js * { * $accountId: $accountSigners * ... * } * ``` * * @param {Transaction} transaction * @return {Object} */ resolve.txSigners = /*#__PURE__*/function () { var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(conf, transaction) { var extra, txSources, signers, index, source, account; return _regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: extra = resolve.extra(conf, transaction); if (!extra.cache.txSigners) { _context3.next = 3; break; } return _context3.abrupt("return", extra.cache.txSigners); case 3: txSources = resolve.txSources(extra, transaction); signers = extra.cache.txSigners = {}; _context3.t0 = _regeneratorRuntime.keys(txSources); case 6: if ((_context3.t1 = _context3.t0()).done) { _context3.next = 15; break; } index = _context3.t1.value; source = txSources[index]; _context3.next = 11; return resolveTxSource(extra, source); case 11: account = _context3.sent; if (!signers[account.id]) { signers[account.id] = account.signers.filter(function (signer) { return signer.type !== "preauthTx"; }); } _context3.next = 6; break; case 15: return _context3.abrupt("return", signers); case 16: case "end": return _context3.stop(); } } }, _callee3); })); return function (_x12, _x13) { return _ref3.apply(this, arguments); }; }(); function resolveTxSource(_x14, _x15) { return _resolveTxSource.apply(this, arguments); } /** * Returns an Array containing the keys for all legit signers of **transaction**. * * @param {Transaction} transaction * @return {Array} */ function _resolveTxSource() { _resolveTxSource = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(conf, address) { return _regeneratorRuntime.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: _context7.prev = 0; _context7.next = 3; return resolve.account(conf, address, "quiet"); case 3: return _context7.abrupt("return", _context7.sent); case 6: _context7.prev = 6; _context7.t0 = _context7["catch"](0); return _context7.abrupt("return", makeAccountResponse(conf, address, "0")); case 9: case "end": return _context7.stop(); } } }, _callee7, null, [[0, 6]]); })); return _resolveTxSource.apply(this, arguments); } resolve.txSignersList = /*#__PURE__*/function () { var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(conf, transaction) { var extra, txSigners; return _regeneratorRuntime.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: extra = resolve.extra(conf, transaction); if (extra.cache.txSignersList) { _context4.next = 6; break; } _context4.next = 4; return resolve.txSigners(extra, transaction); case 4: txSigners = _context4.sent; extra.cache.txSignersList = signersTableToSignersList(txSigners); case 6: return _context4.abrupt("return", extra.cache.txSignersList); case 7: case "end": return _context4.stop(); } } }, _callee4); })); return function (_x16, _x17) { return _ref4.apply(this, arguments); }; }(); function signersTableToSignersList(signersTable) { var array = []; for (var accountId in signersTable) { signersTable[accountId].forEach(function (signer) { if (!array.find(function (key) { return key === signer.key; })) array.push(signer.key); }); } return array; } /** * Add an extra field to **object** that embed cache and local configuration. * * @private */ resolve.extra = function (conf, object) { if (!object._cosmicplus) { misc.setHiddenProperty(object, "_cosmicplus", {}); if (conf.cache) object._cosmicplus.cache = conf.cache;else object._cosmicplus.cache = { destination: {}, account: {} }; object._cosmicplus.network = conf.network; object._cosmicplus.current = conf.current; } return object._cosmicplus; };