UNPKG

@ducatus/ducatus-wallet-client-rev

Version:

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

278 lines (240 loc) 7.12 kB
'use strict'; import { BitcoreLib } from '../../../ducatus-crypto-wallet-core-rev'; import { Constants, Utils } from './common'; const $ = require('preconditions').singleton(); const _ = require('lodash'); const Bitcore = BitcoreLib; const sjcl = require('sjcl'); export class Credentials { static FIELDS = [ 'coin', 'network', 'xPrivKey', // obsolete 'xPrivKeyEncrypted', // obsolte 'xPubKey', 'requestPrivKey', 'requestPubKey', 'copayerId', 'publicKeyRing', 'walletId', 'walletName', 'm', 'n', 'walletPrivKey', 'personalEncryptingKey', 'sharedEncryptingKey', 'copayerName', 'externalSource', 'mnemonic', // Obsolete 'mnemonicEncrypted', // Obsolete 'entropySource', 'mnemonicHasPassphrase', 'derivationStrategy', 'account', 'compliantDerivation', 'addressType', 'hwInfo', // Obsolete 'entropySourcePath', // Obsolete 'use145forBCH', // Obsolete 'version', 'rootPath', // this is only for information 'keyId', // this is only for information 'token' // is this wallet is for a ERC20 token ]; version: number; account: number; walletPrivKey: any; sharedEncryptingKey: any; walletId: any; walletName: any; m: any; n: any; copayerName: any; xPubKey: any; requestPubKey: any; publicKeyRing: any; rootPath: any; derivationStrategy: any; network: string; coin: string; use145forBCH: any; addressType: string; keyId: string; token?: string; externalSource?: boolean; // deprecated property? constructor() { this.version = 2; this.account = 0; } /* *coin, xPrivKey, account, network */ static fromDerivedKey(opts) { $.shouldBeString(opts.coin); $.shouldBeString(opts.network); $.shouldBeNumber(opts.account, 'Invalid account'); $.shouldBeString(opts.xPubKey, 'Invalid xPubKey'); $.shouldBeString(opts.rootPath, 'Invalid rootPath'); $.shouldBeString(opts.keyId, 'Invalid keyId'); $.shouldBeString(opts.requestPrivKey, 'Invalid requestPrivKey'); $.checkArgument(_.isUndefined(opts.nonCompliantDerivation)); opts = opts || {}; var x: any = new Credentials(); x.coin = opts.coin; x.network = opts.network; x.account = opts.account; x.n = opts.n; x.xPubKey = opts.xPubKey; x.keyId = opts.keyId; // this allows to set P2SH in old n=1 wallets if (_.isUndefined(opts.addressType)) { x.addressType = opts.n == 1 ? Constants.SCRIPT_TYPES.P2PKH : Constants.SCRIPT_TYPES.P2SH; } else { x.addressType = opts.addressType; } // Only used for info x.rootPath = opts.rootPath; if (opts.walletPrivKey) { x.addWalletPrivateKey(opts.walletPrivKey); } x.requestPrivKey = opts.requestPrivKey; const priv = Bitcore.PrivateKey(x.requestPrivKey); x.requestPubKey = priv.toPublicKey().toString(); const prefix = 'personalKey'; const entropySource = Bitcore.crypto.Hash.sha256(priv.toBuffer()).toString('hex'); const b = Buffer.from(entropySource, 'hex'); const b2 = Bitcore.crypto.Hash.sha256hmac(b, Buffer.from(prefix)); x.personalEncryptingKey = b2.slice(0, 16).toString('base64'); x.copayerId = Utils.xPubToCopayerId(x.coin, x.xPubKey); x.publicKeyRing = [ { xPubKey: x.xPubKey, requestPubKey: x.requestPubKey } ]; return x; } /* * creates an ERC20 wallet from a ETH wallet */ getTokenCredentials(token: { name: string; symbol: string; address: string }) { const ret = _.cloneDeep(this); ret.walletId = `${ret.walletId}-${token.address}`; ret.coin = token.symbol.toLowerCase(); ret.walletName = token.name; ret.token = token; return ret; } getRootPath() { // This is for OLD v1.0 credentials only. var legacyRootPath = () => { // legacy base path schema var purpose; switch (this.derivationStrategy) { case Constants.DERIVATION_STRATEGIES.BIP45: return "m/45'"; case Constants.DERIVATION_STRATEGIES.BIP44: purpose = '44'; break; case Constants.DERIVATION_STRATEGIES.BIP48: purpose = '48'; break; } var coin = '0'; if (this.network != 'livenet' && Constants.UTXO_COINS.includes(this.coin)) { coin = '1'; } else if (this.coin == 'bch') { if (this.use145forBCH) { coin = '145'; } else { coin = '1025'; } } else if (this.coin == 'btc') { coin = '1025'; } else if (this.coin == 'duc') { coin = '0'; } else if (this.coin == 'eth') { coin = '60'; } else if (this.coin == 'xrp') { coin = '144'; } else if (this.coin == 'ducx') { coin = '1060'; } else { throw new Error('unknown coin: ' + this.coin); } return 'm/' + purpose + "'/" + coin + "'/" + this.account + "'"; }; if (!this.rootPath) { this.rootPath = legacyRootPath(); } return this.rootPath; } static fromObj(obj) { var x: any = new Credentials(); if (!obj.version || obj.version < x.version) { throw new Error('Obsolete credentials version'); } if (obj.version != x.version) { throw new Error('Bad credentials version'); } _.each(Credentials.FIELDS, function(k) { x[k] = obj[k]; }); if (x.externalSource) { throw new Error('External Wallets are no longer supported'); } x.coin = x.coin || 'btc'; x.addressType = x.addressType || Constants.SCRIPT_TYPES.P2SH; x.account = x.account || 0; $.checkState(x.xPrivKey || x.xPubKey || x.xPrivKeyEncrypted, 'invalid input'); return x; } toObj() { var self = this; var x = {}; _.each(Credentials.FIELDS, function(k) { x[k] = self[k]; }); return x; } addWalletPrivateKey(walletPrivKey) { this.walletPrivKey = walletPrivKey; this.sharedEncryptingKey = Utils.privateKeyToAESKey(walletPrivKey); } addWalletInfo(walletId, walletName, m, n, copayerName, opts) { opts = opts || {}; this.walletId = walletId; this.walletName = walletName; this.m = m; if (opts.useNativeSegwit) { this.addressType = n == 1 ? Constants.SCRIPT_TYPES.P2WPKH : Constants.SCRIPT_TYPES.P2WSH; } if (this.n != n && !opts.allowOverwrite) { // we always allow multisig n overwrite if (this.n == 1 || n == 1) { throw new Error(`Bad nr of copayers in addWalletInfo: this: ${this.n} got: ${n}`); } } this.n = n; if (copayerName) this.copayerName = copayerName; if (n == 1) { this.addPublicKeyRing([ { xPubKey: this.xPubKey, requestPubKey: this.requestPubKey } ]); } } hasWalletInfo() { return !!this.walletId; } addPublicKeyRing(publicKeyRing) { this.publicKeyRing = _.clone(publicKeyRing); } isComplete() { if (!this.m || !this.n) return false; if (!this.publicKeyRing || this.publicKeyRing.length != this.n) return false; return true; } }