cosmic-lib
Version:
A JavaScript implementation of the CosmicLink protocol for Stellar
478 lines (390 loc) • 14 kB
JavaScript
"use strict";
/**
* Contains the action methods for CosmicLink.
*
* @private
* @exports action
*/
var _regeneratorRuntime = require("@babel/runtime/regenerator");
var _asyncToGenerator = require("@babel/runtime/helpers/asyncToGenerator");
var action = exports;
var axios = require("@cosmic-plus/base/es5/axios");
var env = require("@cosmic-plus/jsutils/es5/env");
var misc = require("@cosmic-plus/jsutils/es5/misc");
var convert = require("./convert");
var format = env.isBrowser && require("./format");
var resolve = require("./resolve");
var signersUtils = require("./signers-utils");
var status = require("./status");
/**
* Lock a CosmicLink to a network/source pair. If the cosmicLink was created
* from a query/uri/tdesc/json, it will create the corresponding
* transaction/xdr/sep7 formats.
*
* This operation must be performed by the wallet before signing & sending the
* transaction.
*
* @example
* cosmicLib.config.network = "test"
* const cosmicLink = new CosmicLink("?setOptions")
* console.log(cosmicLink.network) // => undefined
* console.log(cosmicLink.xdr) // => undefined
* await cosmicLink.lock()
* console.log(cosmicLink.network) // => "test"
* console.log(cosmicLink.xdr) // => "AAAA...AA=="
*
*
* @alias CosmicLink#lock
* @async
* @param {Object} [options]
* @param {string} options.network Local fallback network
* @param {string} options.horizon Local fallback horizon (overwrited by global configuration)
* @param {string} options.callback Local fallback callback
* @param {string} options.source Local fallback source
*/
action.lock = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(cosmicLink) {
var options,
_args = arguments;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};
if (!cosmicLink.status) {
_context.next = 3;
break;
}
throw new Error(cosmicLink.status);
case 3:
if (!cosmicLink.locker) {
_context.next = 5;
break;
}
throw new Error("CosmicLink is already locked.");
case 5:
_context.prev = 5;
_context.next = 8;
return applyLock(cosmicLink, options);
case 8:
_context.next = 14;
break;
case 10:
_context.prev = 10;
_context.t0 = _context["catch"](5);
if (!cosmicLink.errors) {
console.error(_context.t0);
status.error(cosmicLink, _context.t0.message);
}
status.fail(cosmicLink, "Transaction build failed", "throw");
case 14:
updateSignersNode(cosmicLink);
return _context.abrupt("return", cosmicLink);
case 16:
case "end":
return _context.stop();
}
}
}, _callee, null, [[5, 10]]);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}();
function applyLock(_x2, _x3) {
return _applyLock.apply(this, arguments);
}
/**
* Sign CosmicLink's Transaction with **keypairs_or_preimage** and update the
* other formats accordingly. Only legit signers are allowed to sign, and a
* CosmicLink have to be [locked]{@link CosmicLink#lock} before signing.
*
* @alias CosmicLink#sign
* @param {...Keypair|Buffer|string} ...keypairs_or_preimage
*/
function _applyLock() {
_applyLock = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(cosmicLink, options) {
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
/**
* The locker property tells that a CosmicLink have been locked, and exposes
* the network & source values to which it have been locked.
*
* @alias CosmicLink#locker
*/
cosmicLink.locker = {
source: cosmicLink.tdesc.source || options.source || cosmicLink.config.source,
network: cosmicLink.tdesc.network || options.network || cosmicLink.config.network,
horizon: options.horizon || cosmicLink.horizon,
callback: cosmicLink.tdesc.callback || options.callback || cosmicLink.config.callback
}; /// Preserve the underlying tdesc object.
cosmicLink._tdesc = Object.assign({}, cosmicLink.tdesc, cosmicLink.locker);
delete cosmicLink._query;
delete cosmicLink._json;
if (cosmicLink._transaction) {
_context4.next = 9;
break;
}
_context4.next = 7;
return convert.tdescToTransaction(cosmicLink, cosmicLink.tdesc);
case 7:
cosmicLink._transaction = _context4.sent;
delete cosmicLink._tdesc;
case 9:
delete cosmicLink._transaction._cosmicplus;
_context4.next = 12;
return signersUtils["extends"](cosmicLink, cosmicLink._transaction);
case 12:
case "end":
return _context4.stop();
}
}
}, _callee4);
}));
return _applyLock.apply(this, arguments);
}
action.sign = /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(cosmicLink) {
var transaction,
allFine,
_len,
keypairsOrPreimage,
_key,
index,
keypair,
publicKey,
_short,
_short2,
_short3,
_args2 = arguments;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (cosmicLink.locker) {
_context2.next = 2;
break;
}
throw new Error("cosmicLink is not locked.");
case 2:
transaction = cosmicLink.transaction;
allFine = true;
for (_len = _args2.length, keypairsOrPreimage = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
keypairsOrPreimage[_key - 1] = _args2[_key];
}
if (!(typeof keypairsOrPreimage[0] !== "string")) {
_context2.next = 33;
break;
}
_context2.t0 = _regeneratorRuntime.keys(keypairsOrPreimage);
case 7:
if ((_context2.t1 = _context2.t0()).done) {
_context2.next = 31;
break;
}
index = _context2.t1.value;
keypair = keypairsOrPreimage[index];
publicKey = keypair.publicKey();
if (cosmicLink.transaction.hasSigner(publicKey)) {
_context2.next = 16;
break;
}
_short = misc.shorter(publicKey);
status.error(cosmicLink, "Not a legit signer: " + _short);
allFine = false;
return _context2.abrupt("continue", 7);
case 16:
if (!cosmicLink.transaction.hasSigned(publicKey)) {
_context2.next = 18;
break;
}
return _context2.abrupt("continue", 7);
case 18:
_context2.prev = 18;
transaction.sign(keypair);
_context2.next = 29;
break;
case 22:
_context2.prev = 22;
_context2.t2 = _context2["catch"](18);
console.error(_context2.t2);
_short2 = misc.shorter(publicKey);
status.error(cosmicLink, "Failed to sign with key: " + _short2);
allFine = false;
return _context2.abrupt("continue", 7);
case 29:
_context2.next = 7;
break;
case 31:
_context2.next = 34;
break;
case 33:
try {
transaction.signHashX(keypairsOrPreimage[0]);
} catch (error) {
console.error(error);
_short3 = misc.shorter(keypairsOrPreimage[0]);
status.error(cosmicLink, "Failed to sign with preimage: " + _short3, "throw");
}
case 34:
/// Update other formats.
["_query", "_xdr", "_sep7"].forEach(function (format) {
return delete cosmicLink[format];
});
updateSignersNode(cosmicLink);
if (allFine) {
_context2.next = 40;
break;
}
throw new Error("Some signers where invalid");
case 40:
return _context2.abrupt("return", transaction);
case 41:
case "end":
return _context2.stop();
}
}
}, _callee2, null, [[18, 22]]);
}));
return function (_x4) {
return _ref2.apply(this, arguments);
};
}();
function updateSignersNode(cosmicLink) {
if (cosmicLink._signersNode) {
var signersNode = format.signatures(cosmicLink, cosmicLink._transaction);
cosmicLink.htmlDescription.replaceChild(signersNode, cosmicLink._signersNode);
cosmicLink._signersNode = signersNode;
}
}
/**
* Send CosmicLink's transaction to a blockchain validator, or to
* [StellarGuard]{@link https://stellarguard.me} when relevant. A
* CosmicLink have to be [locked]{@link CosmicLink#lock} before sending.
*
* Returns a promise that resolve/reject to the horizon server response.
*
* @example
* cosmicLink.send()
* .then(console.log)
* .catch(console.error)
*
* @alias CosmicLink#send
* @param {horizon} [horizon] An horizon node URL
* @return {Object} The server response
*/
action.send = /*#__PURE__*/function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(cosmicLink) {
var horizon,
_args3 = arguments;
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
horizon = _args3.length > 1 && _args3[1] !== undefined ? _args3[1] : cosmicLink.horizon;
if (cosmicLink.locker) {
_context3.next = 3;
break;
}
throw new Error("cosmicLink is not locked.");
case 3:
_context3.prev = 3;
if (!cosmicLink.transaction.hasSigner(STELLARGUARD_PUBKEY)) {
_context3.next = 10;
break;
}
_context3.next = 7;
return sendToStellarGuard(cosmicLink);
case 7:
return _context3.abrupt("return", _context3.sent);
case 10:
if (!cosmicLink.callback) {
_context3.next = 16;
break;
}
_context3.next = 13;
return axios.post(cosmicLink.callback, {
xdr: cosmicLink.xdr
});
case 13:
return _context3.abrupt("return", _context3.sent);
case 16:
_context3.next = 18;
return sendToHorizon(cosmicLink, horizon);
case 18:
return _context3.abrupt("return", _context3.sent);
case 19:
_context3.next = 25;
break;
case 21:
_context3.prev = 21;
_context3.t0 = _context3["catch"](3);
if (_context3.t0.response) console.error(_context3.t0.message, _context3.t0.response);
throw _context3.t0;
case 25:
case "end":
return _context3.stop();
}
}
}, _callee3, null, [[3, 21]]);
}));
return function (_x5) {
return _ref3.apply(this, arguments);
};
}();
function sendToHorizon(_x6, _x7) {
return _sendToHorizon.apply(this, arguments);
}
function _sendToHorizon() {
_sendToHorizon = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(cosmicLink, horizon) {
var server;
return _regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
server = resolve.server(cosmicLink, horizon); // Keep connection alive until transaction gets validated or a non-504 error
// is returned. 504 error means the transaction is still following the
// validation process.
// eslint-disable-next-line no-constant-condition
case 1:
if (!true) {
_context5.next = 14;
break;
}
_context5.prev = 2;
_context5.next = 5;
return server.submitTransaction(cosmicLink.transaction);
case 5:
return _context5.abrupt("return", _context5.sent);
case 8:
_context5.prev = 8;
_context5.t0 = _context5["catch"](2);
if (!(_context5.t0.response.status !== 504)) {
_context5.next = 12;
break;
}
throw _context5.t0;
case 12:
_context5.next = 1;
break;
case 14:
case "end":
return _context5.stop();
}
}
}, _callee5, null, [[2, 8]]);
}));
return _sendToHorizon.apply(this, arguments);
}
function sendToStellarGuard(cosmicLink) {
var url = cosmicLink.network === "test" ? "https://test.stellarguard.me/api/transactions" : "https://stellarguard.me/api/transactions";
return axios.post(url, {
xdr: cosmicLink.xdr,
callback: cosmicLink.callback
}).then(function (result) {
return result.data;
});
}
var STELLARGUARD_PUBKEY = "GCVHEKSRASJBD6O2Z532LWH4N2ZLCBVDLLTLKSYCSMBLOYTNMEEGUARD";