UNPKG

laksa-wallet

Version:

Wallet instance for laksa

943 lines (768 loc) 30.1 kB
/** * This source code is being disclosed to you solely for the purpose of your participation in * testing Zilliqa and Laksa. You may view, compile and run the code for that purpose and pursuant to * the protocols and algorithms that are programmed into, and intended by, the code. You may * not do anything else with the code without express permission from Zilliqa Research Pte. Ltd., * including modifying or publishing the code (or any part of it), and developing or forming * another public or private blockchain network. This source code is provided ‘as is’ and no * warranties are given as to title or non-infringement, merchantability or fitness for purpose * and, to the extent permitted by law, all liability for your use of the code is disclaimed. * Some programs in this code are governed by the GNU General Public License v3.0 (available at * https://www.gnu.org/licenses/gpl-3.0.en.html) (‘GPLv3’). The programs that are governed by * GPLv3.0 are those programs that are located in the folders src/depends and tests/depends * and which include a reference to GPLv3 in their program files. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } require('core-js/modules/es6.weak-map'); require('core-js/modules/es7.symbol.async-iterator'); require('core-js/modules/es6.symbol'); require('core-js/modules/es6.promise'); require('core-js/modules/es6.array.sort'); require('core-js/modules/es6.array.iterator'); require('core-js/modules/es6.object.keys'); require('core-js/modules/es6.regexp.split'); require('core-js/modules/es6.regexp.to-string'); require('core-js/modules/web.dom.iterable'); var _regeneratorRuntime = _interopDefault(require('@babel/runtime/regenerator')); require('regenerator-runtime/runtime'); var _asyncToGenerator = _interopDefault(require('@babel/runtime/helpers/asyncToGenerator')); var _classCallCheck = _interopDefault(require('@babel/runtime/helpers/classCallCheck')); var _createClass = _interopDefault(require('@babel/runtime/helpers/createClass')); var _defineProperty = _interopDefault(require('@babel/runtime/helpers/defineProperty')); var _classPrivateFieldSet = _interopDefault(require('@babel/runtime/helpers/classPrivateFieldSet')); var _classPrivateFieldGet = _interopDefault(require('@babel/runtime/helpers/classPrivateFieldGet')); var laksaUtils = require('laksa-utils'); var immutable = require('immutable'); var bip39 = _interopDefault(require('bip39')); var hdkey = _interopDefault(require('hdkey')); var account = require('laksa-account'); var ENCRYPTED = 'ENCRYPTED'; var encryptedBy = { ACCOUNT: 'account', WALLET: 'wallet' }; var Wallet = /*#__PURE__*/ function () { function Wallet(messenger) { var _this = this; _classCallCheck(this, Wallet); _defineProperty(this, "defaultAccount", void 0); _accounts2.set(this, { writable: true, value: immutable.Map({ accounts: immutable.List([]) }) }); _defineProperty(this, "createAccount", function () { var accountInstance = new account.Account(_this.messenger); var accountObject = accountInstance.createAccount(); return _this.addAccount(accountObject); }); _defineProperty(this, "createBatchAccounts", function (number) { if (!laksaUtils.isNumber(number) || laksaUtils.isNumber(number) && number === 0) throw new Error('number has to be >0 Number'); var Batch = []; for (var i = 0; i < number; i += 1) { Batch.push(_this.createAccount()); } return Batch; }); _defineProperty(this, "exportAccountByAddress", /*#__PURE__*/ function () { var _ref = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee(address, password) { var options, accountToExport, result, _args = arguments; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: options = _args.length > 2 && _args[2] !== undefined ? _args[2] : { level: 1024 }; accountToExport = _this.getAccountByAddress(address); if (!accountToExport) { _context.next = 9; break; } _context.next = 5; return accountToExport.toFile(password, options); case 5: result = _context.sent; return _context.abrupt("return", result); case 9: return _context.abrupt("return", false); case 10: case "end": return _context.stop(); } } }, _callee); })); return function (_x, _x2) { return _ref.apply(this, arguments); }; }()); _defineProperty(this, "importAccountFromPrivateKey", function (privateKey) { var accountInstance = new account.Account(_this.messenger); var accountObject = accountInstance.importAccount(privateKey); return _this.addAccount(accountObject); }); _defineProperty(this, "importAccountFromKeyStore", /*#__PURE__*/ function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee2(keyStore, password) { var accountInstance, accountObject; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: accountInstance = new account.Account(_this.messenger); _context2.next = 3; return accountInstance.fromFile(keyStore, password); case 3: accountObject = _context2.sent; return _context2.abrupt("return", _this.addAccount(accountObject)); case 5: case "end": return _context2.stop(); } } }, _callee2); })); return function (_x3, _x4) { return _ref2.apply(this, arguments); }; }()); _defineProperty(this, "removeOneAccountByAddress", function (address) { if (!laksaUtils.isAddress(address)) throw new Error('address is not correct'); var addressRef = _this.getAccountByAddress(address); if (addressRef !== undefined) { var currentArray = _classPrivateFieldGet(_this, _accounts2).get('accounts').toArray(); delete currentArray[addressRef.index]; if (_this.signer !== undefined && addressRef.address === _this.signer.address) { _this.signer = undefined; _this.defaultAccount = undefined; } _classPrivateFieldSet(_this, _accounts2, _classPrivateFieldGet(_this, _accounts2).set('accounts', immutable.List(currentArray))); _classPrivateFieldSet(_this, _accounts2, _classPrivateFieldGet(_this, _accounts2).delete(address)); _this.updateLength(); } _this.updateLength(); }); _defineProperty(this, "getAccountByAddress", function (address) { if (!laksaUtils.isAddress(address)) throw new Error('address is not correct'); return _classPrivateFieldGet(_this, _accounts2).get(address); }); _defineProperty(this, "getAccountByIndex", function (index) { if (!laksaUtils.isNumber(index)) throw new Error('index is not correct'); var address = _classPrivateFieldGet(_this, _accounts2).get('accounts').get(index); if (address !== undefined) { return _this.getAccountByAddress(address); } else return undefined; }); _defineProperty(this, "getWalletAccounts", function () { return _this.getIndexKeys().map(function (index) { var accountFound = _this.getAccountByIndex(parseInt(index, 10)); return accountFound || false; }).filter(function (d) { return !!d; }); }); _defineProperty(this, "cleanAllAccounts", function () { _this.getIndexKeys().forEach(function (index) { return _this.removeOneAccountByIndex(parseInt(index, 10)); }); return true; }); this.length = 0; this.messenger = messenger; this.signer = this.defaultAccount || undefined; } _createClass(Wallet, [{ key: "generateMnemonic", value: function generateMnemonic() { return bip39.generateMnemonic(); } }, { key: "importAccountFromMnemonic", value: function importAccountFromMnemonic(phrase, index) { if (!this.isValidMnemonic(phrase)) { throw new Error("Invalid mnemonic phrase: ".concat(phrase)); } var seed = bip39.mnemonicToSeed(phrase); var hdKey = hdkey.fromMasterSeed(seed); var childKey = hdKey.derive("m/44'/313'/0'/0/".concat(index)); var privateKey = childKey.privateKey.toString('hex'); return this.importAccountFromPrivateKey(privateKey); } }, { key: "isValidMnemonic", value: function isValidMnemonic(phrase) { if (phrase.trim().split(/\s+/g).length < 12) { return false; } return bip39.validateMnemonic(phrase); } }, { key: "defaultSetSigner", value: function defaultSetSigner() { if (this.getWalletAccounts().length === 1 && this.signer === undefined) { this.setSigner(this.getWalletAccounts()[0]); } } /** * @function {updateLength} * @return {number} {wallet account counts} */ }, { key: "updateLength", value: function updateLength() { this.length = this.getIndexKeys().length; } /** * @function {getIndexKeys} * @return {Array<string>} {index keys to the wallet} */ }, { key: "getIndexKeys", value: function getIndexKeys() { var isCorrectKeys = function isCorrectKeys(n) { return /^\d+$/i.test(n) && parseInt(n, 10) <= 9e20; }; var arrays = _classPrivateFieldGet(this, _accounts2).get('accounts').toArray(); return Object.keys(arrays).filter(isCorrectKeys); } /** * @function {getCurrentMaxIndex} * @return {number} {max index to the wallet} */ }, { key: "getCurrentMaxIndex", value: function getCurrentMaxIndex() { var diff = function diff(a, b) { return b - a; }; // const sorted = R.sort(diff, keyList) var sorted = this.getIndexKeys().sort(diff); return sorted[0] === undefined ? -1 : parseInt(sorted[0], 10); } /** * @function {addAccount} * @param {Account} accountObject {account object} * @return {Account} {account object} */ }, { key: "addAccount", value: function addAccount(accountObject) { if (!laksaUtils.isObject(accountObject)) throw new Error('account Object is not correct'); if (this.getAccountByAddress(accountObject.address)) return false; var newAccountObject = accountObject; newAccountObject.createTime = new Date(); newAccountObject.index = this.getCurrentMaxIndex() + 1; var objectKey = newAccountObject.address; var newIndex = newAccountObject.index; var newArrays = _classPrivateFieldGet(this, _accounts2).get('accounts'); newArrays = newArrays.set(newIndex, objectKey); _classPrivateFieldSet(this, _accounts2, _classPrivateFieldGet(this, _accounts2).set(objectKey, newAccountObject)); _classPrivateFieldSet(this, _accounts2, _classPrivateFieldGet(this, _accounts2).set('accounts', immutable.List(newArrays))); // this.#_accounts = this.#_accounts.concat(newArrays) this.updateLength(); this.defaultSetSigner(); return newAccountObject; } /** * @function {createAccount} * @return {Account} {account object} */ }, { key: "importAccountsFromPrivateKeyList", /** * @function {importAccountsFromPrivateKeyList} * @param {Array<PrivateKey>} privateKeyList {list of private keys} * @return {Array<Account>} {array of accounts} */ value: function importAccountsFromPrivateKeyList(privateKeyList) { if (!laksaUtils.isArray(privateKeyList)) throw new Error('privateKeyList has to be Array<String>'); var Imported = []; for (var i = 0; i < privateKeyList.length; i += 1) { Imported.push(this.importAccountFromPrivateKey(privateKeyList[i])); } return Imported; } //------- /** * @function {removeOneAccountByAddress} * @param {Address} address {account address} * @return {undefined} {} */ }, { key: "removeOneAccountByIndex", /** * @function {removeOneAccountByIndex} * @param {number} index {index of account} * @return {undefined} {} */ value: function removeOneAccountByIndex(index) { if (!laksaUtils.isNumber(index)) throw new Error('index is not correct'); var addressRef = this.getAccountByIndex(index); if (addressRef !== undefined && addressRef.address) { this.removeOneAccountByAddress(addressRef.address); } } //--------- /** * @function {getAccountByAddress} * @param {Address} address {account address} * @return {Account} {account object} */ }, { key: "getWalletAddresses", /** * @function {getWalletAddresses} * @return {Array<Address>} {array of address} */ value: function getWalletAddresses() { var _this2 = this; return this.getIndexKeys().map(function (index) { var accountFound = _this2.getAccountByIndex(parseInt(index, 10)); if (accountFound) { return accountFound.address; } return false; }).filter(function (d) { return !!d; }); } /** * @function {getWalletPublicKeys} * @return {Array<PublicKey>} {array of public Key} */ }, { key: "getWalletPublicKeys", value: function getWalletPublicKeys() { var _this3 = this; return this.getIndexKeys().map(function (index) { var accountFound = _this3.getAccountByIndex(parseInt(index, 10)); if (accountFound) { return accountFound.publicKey; } return false; }).filter(function (d) { return !!d; }); } /** * @function {getWalletPrivateKeys} * @return {Array<PrivateKey>} {array of private key} */ }, { key: "getWalletPrivateKeys", value: function getWalletPrivateKeys() { var _this4 = this; return this.getIndexKeys().map(function (index) { var accountFound = _this4.getAccountByIndex(parseInt(index, 10)); if (accountFound) { return accountFound.privateKey; } return false; }).filter(function (d) { return !!d; }); } /** * @function getWalletAccounts * @return {Array<Account>} {array of account} */ }, { key: "updateAccountByAddress", // ----------- /** * @function {updateAccountByAddress} * @param {Address} address {account address} * @param {Account} newObject {account object to be updated} * @return {boolean} {is successful} */ value: function updateAccountByAddress(address, newObject) { if (!laksaUtils.isAddress(address)) throw new Error('address is not correct'); if (!laksaUtils.isObject(newObject)) throw new Error('new account Object is not correct'); var newAccountObject = newObject; newAccountObject.updateTime = new Date(); _classPrivateFieldSet(this, _accounts2, _classPrivateFieldGet(this, _accounts2).update(address, function () { return newAccountObject; })); return true; } // ----------- /** * @function {cleanAllAccountsw} * @return {boolean} {is successful} */ }, { key: "encryptAllAccounts", // ----------- /** * @function {encryptAllAccounts} * @param {string} password {password} * @param {object} options {encryption options} * @return {type} {description} */ value: function () { var _encryptAllAccounts = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee3(password, options) { var keys, results, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, index, accountObject, address, things; return _regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: keys = this.getIndexKeys(); results = []; _iteratorNormalCompletion = true; _didIteratorError = false; _iteratorError = undefined; _context3.prev = 5; for (_iterator = keys[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { index = _step.value; accountObject = this.getAccountByIndex(parseInt(index, 10)); if (accountObject) { address = accountObject.address; things = this.encryptAccountByAddress(address, password, options, encryptedBy.WALLET); results.push(things); } } _context3.next = 13; break; case 9: _context3.prev = 9; _context3.t0 = _context3["catch"](5); _didIteratorError = true; _iteratorError = _context3.t0; case 13: _context3.prev = 13; _context3.prev = 14; if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } case 16: _context3.prev = 16; if (!_didIteratorError) { _context3.next = 19; break; } throw _iteratorError; case 19: return _context3.finish(16); case 20: return _context3.finish(13); case 21: _context3.next = 23; return Promise.all(results); case 23: case "end": return _context3.stop(); } } }, _callee3, this, [[5, 9, 13, 21], [14,, 16, 20]]); })); function encryptAllAccounts(_x5, _x6) { return _encryptAllAccounts.apply(this, arguments); } return encryptAllAccounts; }() /** * @function {decryptAllAccounts} * @param {string} password {decrypt password} * @return {type} {description} */ }, { key: "decryptAllAccounts", value: function () { var _decryptAllAccounts = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee4(password) { var keys, results, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, index, accountObject, address, LastEncryptedBy, things; return _regeneratorRuntime.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: keys = this.getIndexKeys(); results = []; _iteratorNormalCompletion2 = true; _didIteratorError2 = false; _iteratorError2 = undefined; _context4.prev = 5; for (_iterator2 = keys[Symbol.iterator](); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { index = _step2.value; accountObject = this.getAccountByIndex(parseInt(index, 10)); if (accountObject) { address = accountObject.address, LastEncryptedBy = accountObject.LastEncryptedBy; if (LastEncryptedBy === encryptedBy.WALLET) { things = this.decryptAccountByAddress(address, password, encryptedBy.WALLET); results.push(things); } } } _context4.next = 13; break; case 9: _context4.prev = 9; _context4.t0 = _context4["catch"](5); _didIteratorError2 = true; _iteratorError2 = _context4.t0; case 13: _context4.prev = 13; _context4.prev = 14; if (!_iteratorNormalCompletion2 && _iterator2.return != null) { _iterator2.return(); } case 16: _context4.prev = 16; if (!_didIteratorError2) { _context4.next = 19; break; } throw _iteratorError2; case 19: return _context4.finish(16); case 20: return _context4.finish(13); case 21: _context4.next = 23; return Promise.all(results); case 23: case "end": return _context4.stop(); } } }, _callee4, this, [[5, 9, 13, 21], [14,, 16, 20]]); })); function decryptAllAccounts(_x7) { return _decryptAllAccounts.apply(this, arguments); } return decryptAllAccounts; }() /** * @function {encryptAccountByAddress} * @param {Address} address {account address} * @param {string} password {password string for encryption} * @param {object} options {encryption options} * @param {Symbol} by {Symbol that encrypted by} * @return {boolean} {status} */ }, { key: "encryptAccountByAddress", value: function () { var _encryptAccountByAddress = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee5(address, password, options, by) { var accountObject, crypto, encryptedObject, newAccount, tempAccount, updateStatus; return _regeneratorRuntime.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: accountObject = this.getAccountByAddress(address); if (!(accountObject !== undefined)) { _context5.next = 23; break; } crypto = accountObject.crypto; if (!(crypto === undefined)) { _context5.next = 23; break; } encryptedObject = {}; if (!(typeof accountObject.encrypt === 'function')) { _context5.next = 11; break; } _context5.next = 8; return accountObject.encrypt(password, options); case 8: encryptedObject = _context5.sent; _context5.next = 16; break; case 11: newAccount = new account.Account(this.messenger); tempAccount = newAccount.importAccount(accountObject.privateKey); _context5.next = 15; return tempAccount.encrypt(password, options); case 15: encryptedObject = _context5.sent; case 16: encryptedObject.LastEncryptedBy = by || encryptedBy.ACCOUNT; updateStatus = this.updateAccountByAddress(address, encryptedObject); if (!(updateStatus === true)) { _context5.next = 22; break; } return _context5.abrupt("return", encryptedObject); case 22: return _context5.abrupt("return", false); case 23: return _context5.abrupt("return", false); case 24: case "end": return _context5.stop(); } } }, _callee5, this); })); function encryptAccountByAddress(_x8, _x9, _x10, _x11) { return _encryptAccountByAddress.apply(this, arguments); } return encryptAccountByAddress; }() /** * @function {decryptAccountByAddress} * @param {Address} address {account address} * @param {string} password {password string to decrypt} * @param {Symbol} by {Symbol that decrypted by} * @return {boolean} {status} */ }, { key: "decryptAccountByAddress", value: function () { var _decryptAccountByAddress = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee6(address, password, by) { var accountObject, crypto, decryptedObject, decryptedTempObject, newAccount, updateStatus; return _regeneratorRuntime.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: accountObject = this.getAccountByAddress(address); if (!(accountObject !== undefined)) { _context6.next = 23; break; } crypto = accountObject.crypto; if (!laksaUtils.isObject(crypto)) { _context6.next = 23; break; } decryptedObject = {}; if (!(typeof accountObject.decrypt === 'function')) { _context6.next = 11; break; } _context6.next = 8; return accountObject.decrypt(password); case 8: decryptedObject = _context6.sent; _context6.next = 16; break; case 11: _context6.next = 13; return account.decryptAccount(accountObject, password); case 13: decryptedTempObject = _context6.sent; newAccount = new account.Account(this.messenger); decryptedObject = newAccount.importAccount(decryptedTempObject.privateKey); case 16: decryptedObject.LastEncryptedBy = by || encryptedBy.ACCOUNT; updateStatus = this.updateAccountByAddress(address, decryptedObject); if (!(updateStatus === true)) { _context6.next = 22; break; } return _context6.abrupt("return", decryptedObject); case 22: return _context6.abrupt("return", false); case 23: return _context6.abrupt("return", false); case 24: case "end": return _context6.stop(); } } }, _callee6, this); })); function decryptAccountByAddress(_x12, _x13, _x14) { return _decryptAccountByAddress.apply(this, arguments); } return decryptAccountByAddress; }() /** * @function {setSigner} * @param {Account} obj {account object} * @return {Wallet} {wallet instance} */ }, { key: "setSigner", value: function setSigner(obj) { if (laksaUtils.isString(obj)) { this.signer = this.getAccountByAddress(obj); this.defaultAccount = this.getAccountByAddress(obj); } else if (laksaUtils.isObject(obj) && laksaUtils.isAddress(obj.address)) { this.signer = this.getAccountByAddress(obj.address); this.defaultAccount = this.getAccountByAddress(obj.address); } return this; } // sign method for Transaction bytes /** * @function {sign} * @param {Transaction} tx {transaction bytes} * @return {Transaction} {signed transaction object} */ }, { key: "sign", value: function () { var _sign = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee7(tx, _ref3) { var address, password, signerAccount, result; return _regeneratorRuntime.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: address = _ref3.address, password = _ref3.password; if (!(!this.signer && address === undefined)) { _context7.next = 3; break; } throw new Error('This signer is not found or address is not defined'); case 3: _context7.prev = 3; signerAccount = this.getAccountByAddress(address === undefined ? this.signer : address); _context7.next = 7; return signerAccount.signTransaction(tx, password); case 7: result = _context7.sent; return _context7.abrupt("return", result); case 11: _context7.prev = 11; _context7.t0 = _context7["catch"](3); throw _context7.t0; case 14: case "end": return _context7.stop(); } } }, _callee7, this, [[3, 11]]); })); function sign(_x15, _x16) { return _sign.apply(this, arguments); } return sign; }() }, { key: "accounts", get: function get() { return _classPrivateFieldGet(this, _accounts2).get('accounts').toArray(); }, set: function set(value) { if (value !== undefined) { throw new Error('you should not set "accounts" directly, use internal functions'); } } }]); return Wallet; }(); var _accounts2 = new WeakMap(); exports.Wallet = Wallet; exports.ENCRYPTED = ENCRYPTED; exports.encryptedBy = encryptedBy;