UNPKG

@ducatus/ducatus-wallet-client-rev

Version:

Client for @ducatus/ducatus-wallet-service-rev

354 lines 13.9 kB
'use strict'; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); var $ = require('preconditions').singleton(); var _ = __importStar(require("lodash")); var common_1 = require("./common"); var credentials_1 = require("./credentials"); var ducatus_crypto_wallet_core_rev_1 = require("@ducatus/ducatus-crypto-wallet-core-rev"); var Bitcore = ducatus_crypto_wallet_core_rev_1.BitcoreLib; var Mnemonic = require('bitcore-mnemonic'); var sjcl = require('sjcl'); var log = require('./log'); var async = require('async'); var Uuid = require('uuid'); var Errors = require('./errors'); var wordsForLang = { en: Mnemonic.Words.ENGLISH, es: Mnemonic.Words.SPANISH, ja: Mnemonic.Words.JAPANESE, zh: Mnemonic.Words.CHINESE, fr: Mnemonic.Words.FRENCH, it: Mnemonic.Words.ITALIAN }; var NETWORK = 'livenet'; var Key = (function () { function Key() { this.toObj = function () { var self = this; var x = {}; _.each(Key.FIELDS, function (k) { x[k] = self[k]; }); return x; }; this.isPrivKeyEncrypted = function () { return !!this.xPrivKeyEncrypted && !this.xPrivKey; }; this.checkPassword = function (password) { if (this.isPrivKeyEncrypted()) { try { sjcl.decrypt(password, this.xPrivKeyEncrypted); } catch (ex) { return false; } return true; } return null; }; this.get = function (password) { var keys = {}; var fingerPrintUpdated = false; if (this.isPrivKeyEncrypted()) { $.checkArgument(password, 'Private keys are encrypted, a password is needed'); try { keys.xPrivKey = sjcl.decrypt(password, this.xPrivKeyEncrypted); if (!this.fingerPrint) { var xpriv = new Bitcore.HDPrivateKey(keys.xPrivKey); this.fingerPrint = xpriv.fingerPrint.toString('hex'); fingerPrintUpdated = true; } if (this.mnemonicEncrypted) { keys.mnemonic = sjcl.decrypt(password, this.mnemonicEncrypted); } } catch (ex) { throw new Error('Could not decrypt'); } } else { keys.xPrivKey = this.xPrivKey; keys.mnemonic = this.mnemonic; if (fingerPrintUpdated) { keys.fingerPrintUpdated = true; } } return keys; }; this.encrypt = function (password, opts) { if (this.xPrivKeyEncrypted) throw new Error('Private key already encrypted'); if (!this.xPrivKey) throw new Error('No private key to encrypt'); this.xPrivKeyEncrypted = sjcl.encrypt(password, this.xPrivKey, opts); if (!this.xPrivKeyEncrypted) throw new Error('Could not encrypt'); if (this.mnemonic) this.mnemonicEncrypted = sjcl.encrypt(password, this.mnemonic, opts); delete this.xPrivKey; delete this.mnemonic; }; this.decrypt = function (password) { if (!this.xPrivKeyEncrypted) throw new Error('Private key is not encrypted'); try { this.xPrivKey = sjcl.decrypt(password, this.xPrivKeyEncrypted); if (this.mnemonicEncrypted) { this.mnemonic = sjcl.decrypt(password, this.mnemonicEncrypted); } delete this.xPrivKeyEncrypted; delete this.mnemonicEncrypted; } catch (ex) { log.error('error decrypting:', ex); throw new Error('Could not decrypt'); } }; this.derive = function (password, path) { $.checkArgument(path, 'no path at derive()'); var xPrivKey = new Bitcore.HDPrivateKey(this.get(password).xPrivKey, NETWORK); var deriveFn = this.compliantDerivation ? _.bind(xPrivKey.deriveChild, xPrivKey) : _.bind(xPrivKey.deriveNonCompliantChild, xPrivKey); return deriveFn(path); }; this.createCredentials = function (password, opts) { opts = opts || {}; if (password) $.shouldBeString(password, 'provide password'); this._checkCoin(opts.coin); this._checkNetwork(opts.network); $.shouldBeNumber(opts.account, 'Invalid account'); $.shouldBeNumber(opts.n, 'Invalid n'); $.shouldBeUndefined(opts.useLegacyCoinType); $.shouldBeUndefined(opts.useLegacyPurpose); var path = this.getBaseAddressDerivationPath(opts); var xPrivKey = this.derive(password, path); var requestPrivKey = this.derive(password, common_1.Constants.PATHS.REQUEST_KEY).privateKey.toString(); if (opts.network == 'testnet') { var x = xPrivKey.toObject(); x.network = 'testnet'; delete x.xprivkey; delete x.checksum; x.privateKey = _.padStart(x.privateKey, 64, '0'); xPrivKey = new Bitcore.HDPrivateKey(x); } return credentials_1.Credentials.fromDerivedKey({ xPubKey: xPrivKey.hdPublicKey.toString(), coin: opts.coin, network: opts.network, account: opts.account, n: opts.n, rootPath: path, keyId: this.id, requestPrivKey: requestPrivKey, addressType: opts.addressType, walletPrivKey: opts.walletPrivKey }); }; this.createAccess = function (password, opts) { opts = opts || {}; $.shouldBeString(opts.path); var requestPrivKey = new Bitcore.PrivateKey(opts.requestPrivKey || null); var requestPubKey = requestPrivKey.toPublicKey().toString(); var xPriv = this.derive(password, opts.path); var signature = common_1.Utils.signRequestPubKey(requestPubKey, xPriv); requestPrivKey = requestPrivKey.toString(); return { signature: signature, requestPrivKey: requestPrivKey }; }; this.sign = function (rootPath, txp, password, cb) { $.shouldBeString(rootPath); if (this.isPrivKeyEncrypted() && !password) { return cb(new Errors.ENCRYPTED_PRIVATE_KEY()); } var privs = []; var derived = {}; var derived = this.derive(password, rootPath); var xpriv = new Bitcore.HDPrivateKey(derived); var t = common_1.Utils.buildTx(txp); if (common_1.Constants.UTXO_COINS.includes(txp.coin)) { _.each(txp.inputs, function (i) { $.checkState(i.path, 'Input derivation path not available (signing transaction)'); if (!derived[i.path]) { derived[i.path] = xpriv.deriveChild(i.path).privateKey; privs.push(derived[i.path]); } }); var signatures = _.map(privs, function (priv, i) { return t.getSignatures(priv); }); signatures = _.map(_.sortBy(_.flatten(signatures), 'inputIndex'), function (s) { return s.signature.toDER().toString('hex'); }); return signatures; } else { var tx = t.uncheckedSerialize(); tx = typeof tx === 'string' ? [tx] : tx; var chain = common_1.Utils.getChain(txp.coin); var txArray = _.isArray(tx) ? tx : [tx]; var isChange = false; var addressIndex = 0; var _a = ducatus_crypto_wallet_core_rev_1.Deriver.derivePrivateKey(chain, txp.network, derived, addressIndex, isChange), privKey = _a.privKey, pubKey = _a.pubKey; var signatures_1 = []; for (var _i = 0, txArray_1 = txArray; _i < txArray_1.length; _i++) { var rawTx = txArray_1[_i]; var signed = ducatus_crypto_wallet_core_rev_1.Transactions.getSignature({ chain: chain, tx: rawTx, key: { privKey: privKey, pubKey: pubKey } }); signatures_1.push(signed); } return signatures_1; } }; this.version = 1; this.use0forBCH = false; this.use44forMultisig = false; this.compliantDerivation = true; this.id = Uuid.v4(); } Key.match = function (a, b) { return a.id == b.id; }; Key.prototype._checkCoin = function (coin) { if (!_.includes(common_1.Constants.COINS, coin)) throw new Error('Invalid coin'); }; Key.prototype._checkNetwork = function (network) { if (!_.includes(['livenet', 'testnet'], network)) throw new Error('Invalid network'); }; Key.prototype.getBaseAddressDerivationPath = function (opts) { $.checkArgument(opts, 'Need to provide options'); $.checkArgument(opts.n >= 1, 'n need to be >=1'); var purpose = opts.n == 1 || this.use44forMultisig ? '44' : '48'; var coinCode = '0'; if (opts.network == 'testnet' && common_1.Constants.UTXO_COINS.includes(opts.coin)) { coinCode = '1'; } else if (opts.coin == 'bch') { if (this.use0forBCH) { coinCode = '1025'; } else { coinCode = '145'; } } else if (opts.coin == 'btc') { coinCode = '1025'; } else if (opts.coin == 'duc') { coinCode = '0'; } else if (opts.coin == 'eth') { coinCode = '60'; } else if (opts.coin == 'xrp') { coinCode = '144'; } else if (opts.coin == 'ducx') { coinCode = '1060'; } else { throw new Error('unknown coin: ' + opts.coin); } return 'm/' + purpose + "'/" + coinCode + "'/" + opts.account + "'"; }; Key.FIELDS = [ 'xPrivKey', 'xPrivKeyEncrypted', 'mnemonic', 'mnemonicEncrypted', 'mnemonicHasPassphrase', 'fingerPrint', 'compliantDerivation', 'BIP45', 'use0forBCH', 'use44forMultisig', 'version', 'id' ]; Key.create = function (opts) { opts = opts || {}; if (opts.language && !wordsForLang[opts.language]) throw new Error('Unsupported language'); var m = new Mnemonic(wordsForLang[opts.language]); while (!Mnemonic.isValid(m.toString())) { m = new Mnemonic(wordsForLang[opts.language]); } var x = new Key(); var xpriv = m.toHDPrivateKey(opts.passphrase, NETWORK); x.xPrivKey = xpriv.toString(); x.fingerPrint = xpriv.fingerPrint.toString('hex'); x.mnemonic = m.phrase; x.mnemonicHasPassphrase = !!opts.passphrase; x.use0forBCH = opts.useLegacyCoinType; x.use44forMultisig = opts.useLegacyPurpose; x.compliantDerivation = !opts.nonCompliantDerivation; return x; }; Key.fromMnemonic = function (words, opts) { $.checkArgument(words); if (opts) $.shouldBeObject(opts); opts = opts || {}; var m = new Mnemonic(words); var x = new Key(); var xpriv = m.toHDPrivateKey(opts.passphrase, NETWORK); x.xPrivKey = xpriv.toString(); x.fingerPrint = xpriv.fingerPrint.toString('hex'); x.mnemonic = words; x.mnemonicHasPassphrase = !!opts.passphrase; x.use0forBCH = opts.useLegacyCoinType; x.use44forMultisig = opts.useLegacyPurpose; x.compliantDerivation = !opts.nonCompliantDerivation; return x; }; Key.fromExtendedPrivateKey = function (xPriv, opts) { $.checkArgument(xPriv); opts = opts || {}; var xpriv; try { xpriv = new Bitcore.HDPrivateKey(xPriv); } catch (e) { throw new Error('Invalid argument'); } var x = new Key(); x.xPrivKey = xpriv.toString(); x.fingerPrint = xpriv.fingerPrint.toString('hex'); x.mnemonic = null; x.mnemonicHasPassphrase = null; x.use44forMultisig = opts.useLegacyPurpose; x.use0forBCH = opts.useLegacyCoinType; x.compliantDerivation = !opts.nonCompliantDerivation; return x; }; Key.fromObj = function (obj) { $.shouldBeObject(obj); var x = new Key(); if (obj.version != x.version) { throw new Error('Bad Key version'); } _.each(Key.FIELDS, function (k) { x[k] = obj[k]; }); $.checkState(x.xPrivKey || x.xPrivKeyEncrypted, 'invalid input'); return x; }; return Key; }()); exports.Key = Key; //# sourceMappingURL=key.js.map