UNPKG

trezor-link

Version:
874 lines (731 loc) 30.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _monkey_patch = require("./protobuf/monkey_patch"); var _defered = require("../defered"); var _parse_protocol = require("./protobuf/parse_protocol"); var _send = require("./send"); var _receive = require("./receive"); var _debugDecorator = require("../debug-decorator"); var _sharedConnectionWorker = require("./sharedConnectionWorker"); var _class, _temp; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; } (0, _monkey_patch.patch)(); // eslint-disable-next-line quotes var stringify = require('json-stable-stringify'); function stableStringify(devices) { if (devices == null) { return "null"; } var pureDevices = devices.map(function (device) { var path = device.path; var session = device.session == null ? null : device.session; return { path: path, session: session }; }); return stringify(pureDevices); } function compare(a, b) { if (!isNaN(parseInt(a.path))) { return parseInt(a.path) - parseInt(b.path); } else { return a.path < b.path ? -1 : a.path > b.path ? 1 : 0; } } var ITER_MAX = 60; var ITER_DELAY = 500; var LowlevelTransportWithSharedConnections = (_class = (_temp = /*#__PURE__*/function () { // path => promise rejecting on release function LowlevelTransportWithSharedConnections(plugin, sharedWorkerFactory) { _classCallCheck(this, LowlevelTransportWithSharedConnections); this.name = "LowlevelTransportWithSharedConnections"; this.debug = false; this.deferedDebugOnRelease = {}; this.deferedNormalOnRelease = {}; this.configured = false; this.stopped = false; this._lastStringified = ""; this.requestNeeded = false; this.latestId = 0; this.defereds = {}; this.isOutdated = false; this.plugin = plugin; this.version = plugin.version; this._sharedWorkerFactory = sharedWorkerFactory; if (!this.plugin.allowsWriteAndEnumerate) { // This should never happen anyway throw new Error("Plugin with shared connections cannot disallow write and enumerate"); } } _createClass(LowlevelTransportWithSharedConnections, [{ key: "enumerate", value: function enumerate() { return this._silentEnumerate(); } }, { key: "_silentEnumerate", value: function () { var _silentEnumerate2 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee() { var devices, sessionsM, debugSessions, normalSessions, devicesWithSessions; return _regenerator["default"].wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return this.sendToWorker({ type: "enumerate-intent" }); case 2: devices = []; _context.prev = 3; _context.next = 6; return this.plugin.enumerate(); case 6: devices = _context.sent; case 7: _context.prev = 7; _context.next = 10; return this.sendToWorker({ type: "enumerate-done" }); case 10: return _context.finish(7); case 11: _context.next = 13; return this.sendToWorker({ type: "get-sessions-and-disconnect", devices: devices }); case 13: sessionsM = _context.sent; if (!(sessionsM.type !== "sessions")) { _context.next = 16; break; } throw new Error("Wrong reply"); case 16: debugSessions = sessionsM.debugSessions; normalSessions = sessionsM.normalSessions; devicesWithSessions = devices.map(function (device) { var session = normalSessions[device.path]; var debugSession = debugSessions[device.path]; return { path: device.path, session: session, debug: device.debug, debugSession: debugSession }; }); this._releaseDisconnected(devicesWithSessions); return _context.abrupt("return", devicesWithSessions.sort(compare)); case 21: case "end": return _context.stop(); } } }, _callee, this, [[3,, 7, 11]]); })); function _silentEnumerate() { return _silentEnumerate2.apply(this, arguments); } return _silentEnumerate; }() }, { key: "_releaseDisconnected", value: function _releaseDisconnected(devices) { var _this = this; var connected = {}; devices.forEach(function (device) { if (device.session != null) { connected[device.session] = true; } }); Object.keys(this.deferedDebugOnRelease).forEach(function (session) { if (connected[session] == null) { _this._releaseCleanup(session, true); } }); Object.keys(this.deferedNormalOnRelease).forEach(function (session) { if (connected[session] == null) { _this._releaseCleanup(session, false); } }); } }, { key: "listen", value: function () { var _listen = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee2(old) { var oldStringified, last; return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: oldStringified = stableStringify(old); last = old == null ? this._lastStringified : oldStringified; return _context2.abrupt("return", this._runIter(0, last)); case 3: case "end": return _context2.stop(); } } }, _callee2, this); })); function listen(_x) { return _listen.apply(this, arguments); } return listen; }() }, { key: "_runIter", value: function () { var _runIter2 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee3(iteration, oldStringified) { var devices, stringified; return _regenerator["default"].wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.next = 2; return this._silentEnumerate(); case 2: devices = _context3.sent; stringified = stableStringify(devices); if (!(stringified !== oldStringified || iteration === ITER_MAX)) { _context3.next = 7; break; } this._lastStringified = stringified; return _context3.abrupt("return", devices); case 7: _context3.next = 9; return (0, _defered.resolveTimeoutPromise)(ITER_DELAY, null); case 9: return _context3.abrupt("return", this._runIter(iteration + 1, stringified)); case 10: case "end": return _context3.stop(); } } }, _callee3, this); })); function _runIter(_x2, _x3) { return _runIter2.apply(this, arguments); } return _runIter; }() }, { key: "acquire", value: function () { var _acquire = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee4(input, debugLink) { var messBack, reset, messBack2, session; return _regenerator["default"].wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: _context4.next = 2; return this.sendToWorker({ type: "acquire-intent", path: input.path, previous: input.previous, debug: debugLink }); case 2: messBack = _context4.sent; if (!(messBack.type === "wrong-previous-session")) { _context4.next = 5; break; } throw new Error("wrong previous session"); case 5: if (!(messBack.type !== "other-session")) { _context4.next = 7; break; } throw new Error("Strange reply"); case 7: reset = messBack.otherSession == null; _context4.prev = 8; _context4.next = 11; return this.plugin.connect(input.path, debugLink, reset); case 11: _context4.next = 18; break; case 13: _context4.prev = 13; _context4.t0 = _context4["catch"](8); _context4.next = 17; return this.sendToWorker({ type: "acquire-failed" }); case 17: throw _context4.t0; case 18: _context4.next = 20; return this.sendToWorker({ type: "acquire-done" }); case 20: messBack2 = _context4.sent; if (!(messBack2.type !== "session-number")) { _context4.next = 23; break; } throw new Error("Strange reply."); case 23: session = messBack2.number; if (debugLink) { this.deferedDebugOnRelease[session] = (0, _defered.create)(); } else { this.deferedNormalOnRelease[session] = (0, _defered.create)(); } return _context4.abrupt("return", session); case 26: case "end": return _context4.stop(); } } }, _callee4, this, [[8, 13]]); })); function acquire(_x4, _x5) { return _acquire.apply(this, arguments); } return acquire; }() }, { key: "release", value: function () { var _release = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee5(session, onclose, debugLink) { var messback, path, otherSession, last; return _regenerator["default"].wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: if (!(onclose && !debugLink)) { _context5.next = 3; break; } // if we wait for worker messages, shared worker survives // and delays closing // so we "fake" release this.sendToWorker({ type: "release-onclose", session: session }); return _context5.abrupt("return"); case 3: _context5.next = 5; return this.sendToWorker({ type: "release-intent", session: session, debug: debugLink }); case 5: messback = _context5.sent; if (!(messback.type === "double-release")) { _context5.next = 8; break; } throw new Error("Trying to double release."); case 8: if (!(messback.type !== "path")) { _context5.next = 10; break; } throw new Error("Strange reply."); case 10: path = messback.path; otherSession = messback.otherSession; last = otherSession == null; this._releaseCleanup(session, debugLink); _context5.prev = 14; _context5.next = 17; return this.plugin.disconnect(path, debugLink, last); case 17: _context5.next = 21; break; case 19: _context5.prev = 19; _context5.t0 = _context5["catch"](14); case 21: _context5.next = 23; return this.sendToWorker({ type: "release-done" }); case 23: case "end": return _context5.stop(); } } }, _callee5, this, [[14, 19]]); })); function release(_x6, _x7, _x8) { return _release.apply(this, arguments); } return release; }() }, { key: "_releaseCleanup", value: function _releaseCleanup(session, debugLink) { var table = debugLink ? this.deferedDebugOnRelease : this.deferedNormalOnRelease; if (table[session] != null) { table[session].reject(new Error("Device released or disconnected")); delete table[session]; } } }, { key: "configure", value: function () { var _configure = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee6(signedData) { var messages; return _regenerator["default"].wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: messages = (0, _parse_protocol.parseConfigure)(signedData); this._messages = messages; this.configured = true; case 3: case "end": return _context6.stop(); } } }, _callee6, this); })); function configure(_x9) { return _configure.apply(this, arguments); } return configure; }() }, { key: "_sendLowlevel", value: function _sendLowlevel(path, debug) { var _this2 = this; return function (data) { return _this2.plugin.send(path, data, debug); }; } }, { key: "_receiveLowlevel", value: function _receiveLowlevel(path, debug) { var _this3 = this; return function () { return _this3.plugin.receive(path, debug); }; } }, { key: "messages", value: function messages() { if (this._messages == null) { throw new Error("Transport not configured."); } return this._messages; } }, { key: "doWithSession", value: function () { var _doWithSession = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee7(session, debugLink, inside) { var sessionsM, sessionsMM, path_, path, resPromise, defered; return _regenerator["default"].wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: _context7.next = 2; return this.sendToWorker({ type: "get-sessions" }); case 2: sessionsM = _context7.sent; if (!(sessionsM.type !== "sessions")) { _context7.next = 5; break; } throw new Error("Wrong reply"); case 5: sessionsMM = debugLink ? sessionsM.debugSessions : sessionsM.normalSessions; path_ = null; Object.keys(sessionsMM).forEach(function (kpath) { if (sessionsMM[kpath] === session) { path_ = kpath; } }); if (!(path_ == null)) { _context7.next = 10; break; } throw new Error("Session not available."); case 10: path = path_; _context7.next = 13; return inside(path); case 13: resPromise = _context7.sent; defered = debugLink ? this.deferedDebugOnRelease[session] : this.deferedNormalOnRelease[session]; return _context7.abrupt("return", Promise.race([defered.rejectingPromise, resPromise])); case 16: case "end": return _context7.stop(); } } }, _callee7, this); })); function doWithSession(_x10, _x11, _x12) { return _doWithSession.apply(this, arguments); } return doWithSession; }() }, { key: "call", value: function () { var _call = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee9(session, name, data, debugLink) { var _this4 = this; var callInside; return _regenerator["default"].wrap(function _callee9$(_context9) { while (1) { switch (_context9.prev = _context9.next) { case 0: callInside = /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee8(path) { var messages, message; return _regenerator["default"].wrap(function _callee8$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: messages = _this4.messages(); _context8.next = 3; return (0, _send.buildAndSend)(messages, _this4._sendLowlevel(path, debugLink), name, data); case 3: _context8.next = 5; return (0, _receive.receiveAndParse)(messages, _this4._receiveLowlevel(path, debugLink)); case 5: message = _context8.sent; return _context8.abrupt("return", message); case 7: case "end": return _context8.stop(); } } }, _callee8); })); return function callInside(_x17) { return _ref.apply(this, arguments); }; }(); return _context9.abrupt("return", this.doWithSession(session, debugLink, callInside)); case 2: case "end": return _context9.stop(); } } }, _callee9, this); })); function call(_x13, _x14, _x15, _x16) { return _call.apply(this, arguments); } return call; }() }, { key: "post", value: function () { var _post = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee11(session, name, data, debugLink) { var _this5 = this; var callInside; return _regenerator["default"].wrap(function _callee11$(_context11) { while (1) { switch (_context11.prev = _context11.next) { case 0: callInside = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee10(path) { var messages; return _regenerator["default"].wrap(function _callee10$(_context10) { while (1) { switch (_context10.prev = _context10.next) { case 0: messages = _this5.messages(); _context10.next = 3; return (0, _send.buildAndSend)(messages, _this5._sendLowlevel(path, debugLink), name, data); case 3: case "end": return _context10.stop(); } } }, _callee10); })); return function callInside(_x22) { return _ref2.apply(this, arguments); }; }(); return _context11.abrupt("return", this.doWithSession(session, debugLink, callInside)); case 2: case "end": return _context11.stop(); } } }, _callee11, this); })); function post(_x18, _x19, _x20, _x21) { return _post.apply(this, arguments); } return post; }() }, { key: "read", value: function () { var _read = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee13(session, debugLink) { var _this6 = this; var callInside; return _regenerator["default"].wrap(function _callee13$(_context13) { while (1) { switch (_context13.prev = _context13.next) { case 0: callInside = /*#__PURE__*/function () { var _ref3 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee12(path) { var messages, message; return _regenerator["default"].wrap(function _callee12$(_context12) { while (1) { switch (_context12.prev = _context12.next) { case 0: messages = _this6.messages(); _context12.next = 3; return (0, _receive.receiveAndParse)(messages, _this6._receiveLowlevel(path, debugLink)); case 3: message = _context12.sent; return _context12.abrupt("return", message); case 5: case "end": return _context12.stop(); } } }, _callee12); })); return function callInside(_x25) { return _ref3.apply(this, arguments); }; }(); return _context13.abrupt("return", this.doWithSession(session, debugLink, callInside)); case 2: case "end": return _context13.stop(); } } }, _callee13, this); })); function read(_x23, _x24) { return _read.apply(this, arguments); } return read; }() }, { key: "init", value: function () { var _init = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee14(debug) { var _this7 = this; return _regenerator["default"].wrap(function _callee14$(_context14) { while (1) { switch (_context14.prev = _context14.next) { case 0: this.debug = !!debug; this.requestNeeded = this.plugin.requestNeeded; _context14.next = 4; return this.plugin.init(debug); case 4: // create the worker ONLY when the plugin is successfully inited if (this._sharedWorkerFactory != null) { this.sharedWorker = this._sharedWorkerFactory(); if (this.sharedWorker != null) { this.sharedWorker.port.onmessage = function (e) { // $FlowIssue _this7.receiveFromWorker(e.data); }; } } case 5: case "end": return _context14.stop(); } } }, _callee14, this); })); function init(_x26) { return _init.apply(this, arguments); } return init; }() }, { key: "requestDevice", value: function () { var _requestDevice = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee15() { return _regenerator["default"].wrap(function _callee15$(_context15) { while (1) { switch (_context15.prev = _context15.next) { case 0: return _context15.abrupt("return", this.plugin.requestDevice()); case 1: case "end": return _context15.stop(); } } }, _callee15, this); })); function requestDevice() { return _requestDevice.apply(this, arguments); } return requestDevice; }() }, { key: "sendToWorker", value: function sendToWorker(message) { var _this8 = this; if (this.stopped) { return Promise.reject("Transport stopped."); } this.latestId++; var id = this.latestId; this.defereds[id] = (0, _defered.create)(); // when shared worker is not loaded as a shared loader, use it as a module instead if (this.sharedWorker != null) { this.sharedWorker.port.postMessage({ id: id, message: message }); } else { (0, _sharedConnectionWorker.postModuleMessage)({ id: id, message: message }, function (m) { return _this8.receiveFromWorker(m); }); } return this.defereds[id].promise; } }, { key: "receiveFromWorker", value: function receiveFromWorker(m) { this.defereds[m.id].resolve(m.message); delete this.defereds[m.id]; } }, { key: "setBridgeLatestUrl", value: function setBridgeLatestUrl(url) {} }, { key: "setBridgeLatestVersion", value: function setBridgeLatestVersion(version) {} }, { key: "stop", value: function stop() { this.stopped = true; this.sharedWorker = null; } }]); return LowlevelTransportWithSharedConnections; }(), _temp), (_applyDecoratedDescriptor(_class.prototype, "enumerate", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "enumerate"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "listen", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "listen"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "acquire", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "acquire"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "release", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "release"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "configure", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "configure"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "call", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "call"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "post", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "post"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "read", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "read"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "init", [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, "init"), _class.prototype)), _class); exports["default"] = LowlevelTransportWithSharedConnections; module.exports = exports.default;