cosmic-lib
Version:
A JavaScript implementation of the CosmicLink protocol for Stellar
593 lines (491 loc) • 17.3 kB
JavaScript
"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;
};