diff --git a/app-template/package-template.json b/app-template/package-template.json index 7f7dc5d84..22e23c632 100644 --- a/app-template/package-template.json +++ b/app-template/package-template.json @@ -56,7 +56,7 @@ "bezier-easing": "^2.0.3", "bhttp": "1.2.1", "bitauth": "^0.2.1", - "bitcore-wallet-client": "6.0.1", + "bitcore-wallet-client": "6.1.0", "bower": "^1.7.9", "cordova-android": "5.1.1", "cordova-custom-config": "^3.0.5", diff --git a/src/js/controllers/cashScan.js b/src/js/controllers/cashScan.js index 311502aab..4bc429e07 100644 --- a/src/js/controllers/cashScan.js +++ b/src/js/controllers/cashScan.js @@ -1,48 +1,62 @@ - 'use strict'; angular.module('copayApp.controllers').controller('cashScanController', - function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, profileService, walletService, configService, $log, txFormatService, bwcError, pushNotificationsService, bwcService) { + function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, profileService, walletService, configService, $log, txFormatService, bwcError, pushNotificationsService, bwcService) { var wallet; var errors = bwcService.getErrors(); - $scope.error = null; + $scope.error = null; $scope.$on("$ionicView.enter", function(event, data) { updateAllWallets(); }); var updateAllWallets = function() { - var wallets = profileService.getWallets({coin:'btc', onlyComplete:true, network: 'livenet' }); + var wallets1 = profileService.getWallets({ + coin: 'btc', + onlyComplete: true, + network: 'livenet' + }); + + if (lodash.isEmpty(wallets1)) { + $state.go('tabs.home'); + return; + } + + // Filter out already duplicated wallets + var walletsBCH = profileService.getWallets({ + coin: 'bch', + network: 'livenet' + }); + var xPubKeyIndex = lodash.indexBy(walletsBCH, "credentials.xPubKey"); + + wallets1 = lodash.filter(wallets1, function(w) { + return !xPubKeyIndex[w.credentials.xPubKey]; + }); - var kk = lodash.indexBy(wallets,"credentials.xPubKey"); - - - // TODO ? - if (lodash.isEmpty(wallets)) return; - - - var walletsBCH = profileService.getWallets({coin:'bch', network: 'livenet' }); - var xPubKeyIndex = lodash.indexBy(walletsBCH,"credentials.xPubKey"); - -// wallets= lodash.filter(wallets,function(w) { return !xPubKeyIndex[w.credentials.xPubKey]; }); + // Filter out non BIP44 wallets + var wallets = lodash.filter(wallets1, function(w) { + return w.credentials.derivationStrategy == 'BIP44' + }); $scope.wallets = wallets; + $scope.nonBIP44 = wallets1.length != wallets.length; var i = wallets.length; var j = 0; lodash.each(wallets, function(wallet) { - walletService.getBalance(wallet, {coin:'bch'}, function(err, balance) { + walletService.getBalance(wallet, { + coin: 'bch' + }, function(err, balance) { if (err) { wallet.error = (err === 'WALLET_NOT_REGISTERED') ? gettextCatalog.getString('Wallet not registered') : bwcError.msg(err); $log.error(err); return; - } -// + } + // -console.log('[otherBalance.js.28:balance:]',balance); //TODO wallet.error = null; wallet.bchBalance = txFormatService.formatAmountStr('bch', balance.availableAmount); if (++j == i) { @@ -58,24 +72,26 @@ console.log('[otherBalance.js.28:balance:]',balance); //TODO }; $scope.duplicate = function(wallet) { - $scope.error = null; - $log.debug('Duplicating wallet for BCH:' + wallet.id + ':' + wallet.name); + $scope.error = null; + $log.debug('Duplicating wallet for BCH:' + wallet.id + ':' + wallet.name); var opts = {}; opts.name = wallet.name + '[BCH]'; opts.m = wallet.m; opts.n = wallet.n; - opts.myName = wallet.credentials.copayerName; + opts.myName = wallet.credentials.copayerName; opts.networkName = wallet.network; opts.coin = 'bch'; + opts.walletPrivKey = wallet.credentials.walletPrivKey; + opts.compliantDerivation = wallet.credentials.compliantDerivation; - // TODO: finger print / decrypt - $log.warn('TODO finger print / decrypt'); - opts.extendedPrivateKey = wallet.credentials.xPrivKey; function setErr(err, cb) { - $scope.error = bwcError.cb(err, gettextCatalog.getString('Could not duplicate'), function() { - return cb(err); + + if (!cb) cb = function() {}; + + $scope.error = bwcError.cb(err, gettextCatalog.getString('Could not duplicate'), function() { + return cb(err); }); $timeout(function() { $rootScope.$apply(); @@ -83,35 +99,70 @@ console.log('[otherBalance.js.28:balance:]',balance); //TODO } function importOrCreate(cb) { - walletService.getStatus(wallet, {}, function(err, status){ + walletService.getStatus(wallet, {}, function(err, status) { if (err) return cb(err); - opts.singleAddress = status.wallet.singleAddress; + opts.singleAddress = status.wallet.singleAddress; // first try to import - profileService.importExtendedPrivateKey(opts.extendedPrivateKey, opts, function(err, client) { - if (err && !(err instanceof errors.NOT_AUTHORIZED) ) { + profileService.importExtendedPrivateKey(opts.extendedPrivateKey, opts, function(err, newWallet) { + if (err && !(err instanceof errors.NOT_AUTHORIZED)) { return setErr(err, cb); } if (err) { // create and store a wallet - return profileService.createWallet(opts, function(err, client) { + return profileService.createWallet(opts, function(err, newWallet) { if (err) return setErr(err, cb); - return cb(null, client, true); + return cb(null, newWallet, true); }); } - return cb(null, client); + return cb(null, newWallet); }); }); }; + // Multisig wallets? add Copayers + function addCopayers(newWallet, isNew, cb) { + if (!isNew) return cb(); + if (wallet.n == 1) return cb(); - importOrCreate(function(err, client, isNew) { - if (err) return; - walletService.updateRemotePreferences(client); - pushNotificationsService.updateSubscription(client); - walletService.startScan(wallet, function() { }); - $state.go('tabs.home'); + $log.info('Adding copayers for BCH wallet config:' + wallet.m + '-' + wallet.n); + + walletService.copyCopayers(wallet, newWallet, function(err) { + if (err) return setErr(err, cb); + + return cb(); + }); + }; + + walletService.getKeys(wallet,function(err,keys) { + if (err) { + $scope.error = err; + return $timeout(function() { + $rootScope.$apply(); + }, 10); + } + opts.extendedPrivateKey = keys.xPrivKey; + ongoingProcess.set('duplicatingWallet', true); + importOrCreate(function(err, newWallet, isNew) { + if (err) { + ongoingProcess.set('duplicatingWallet', false); + return; + } + walletService.updateRemotePreferences(newWallet); + pushNotificationsService.updateSubscription(newWallet); + + addCopayers(newWallet, isNew, function(err) { + ongoingProcess.set('duplicatingWallet', false); + if (err) + return setErr(err); + + if (isNew) + walletService.startScan(newWallet, function() {}); + + $state.go('tabs.home'); + }); + }); }); } }); diff --git a/src/js/controllers/tab-home.js b/src/js/controllers/tab-home.js index 79be14391..a3a614e21 100644 --- a/src/js/controllers/tab-home.js +++ b/src/js/controllers/tab-home.js @@ -224,6 +224,7 @@ angular.module('copayApp.controllers').controller('tabHomeController', wallet.error = null; wallet.status = status; + wallet.updating = status && status.wallet && status.wallet.scanStatus == 'running'; // TODO service refactor? not in profile service profileService.setLastKnownBalance(wallet.id, wallet.status.totalBalanceStr, function() {}); } @@ -242,6 +243,7 @@ angular.module('copayApp.controllers').controller('tabHomeController', return; } wallet.status = status; + wallet.updating = status && status.wallet && status.wallet.scanStatus == 'running'; updateTxps(); }); }; diff --git a/src/js/services/onGoingProcess.js b/src/js/services/onGoingProcess.js index e0f82fc40..94d746202 100644 --- a/src/js/services/onGoingProcess.js +++ b/src/js/services/onGoingProcess.js @@ -45,7 +45,8 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti 'cancelingGiftCard': 'Canceling Gift Card...', 'creatingGiftCard': 'Creating Gift Card...', 'buyingGiftCard': 'Buying Gift Card...', - 'topup': gettext('Top up in progress...') + 'topup': gettext('Top up in progress...'), + 'duplicatingWallet': gettext('Duplicating wallet...'), }; root.clear = function() { diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index d944eca43..f93fc03a8 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -386,7 +386,6 @@ angular.module('copayApp.services') } } } - return cb(null, walletClient); }; // Creates a wallet on BWC/BWS diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js index 3768cecaa..b456fae61 100644 --- a/src/js/services/walletService.js +++ b/src/js/services/walletService.js @@ -259,6 +259,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim }; function cacheStatus(status) { + if (status.wallet && status.wallet.scanStatus == 'running') return; + wallet.cachedStatus = status || {}; var cache = wallet.cachedStatus; cache.statusUpdatedOn = Date.now(); @@ -931,15 +933,17 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim // Approx utxo amount, from which the uxto is economically redeemable root.getLowAmount = function(wallet, feeLevels, nbOutputs) { - var minFee = root.getMinFee(wallet,feeLevels, nbOutputs); - return parseInt( minFee / LOW_AMOUNT_RATIO); + var minFee = root.getMinFee(wallet, feeLevels, nbOutputs); + return parseInt(minFee / LOW_AMOUNT_RATIO); }; root.getLowUtxos = function(wallet, levels, cb) { - wallet.getUtxos({coin: wallet.coin}, function(err, resp) { + wallet.getUtxos({ + coin: wallet.coin + }, function(err, resp) { if (err || !resp || !resp.length) return cb(); var minFee = root.getMinFee(wallet, levels, resp.length); @@ -955,7 +959,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim var totalLow = lodash.sum(lowUtxos, 'satoshis'); return cb(err, { - allUtxos: resp || [], + allUtxos: resp || [], lowUtxos: lowUtxos || [], warning: minFee / balance > TOTAL_LOW_WARNING_RATIO, minFee: minFee, @@ -1237,5 +1241,33 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim else return 'bitcoin'; } + + root.copyCopayers = function(wallet, newWallet, cb) { + var c = wallet.credentials; + + var walletPrivKey = bitcore.PrivateKey.fromString(c.walletPrivKey); + + var copayer = 1, + i = 0, + l = c.publicKeyRing.length; + var mainErr = null; + + lodash.each(c.publicKeyRing, function(item) { + var name = item.copayerName || ('copayer ' + copayer++); + newWallet._doJoinWallet(newWallet.credentials.walletId, walletPrivKey, item.xPubKey, item.requestPubKey, name, { + coin: newWallet.credentials.coin, + }, function(err) { + //Ignore error is copayer already in wallet + if (err && !(err instanceof errors.COPAYER_IN_WALLET)) { + mainErr = err; + } + + if (++i == l) { + return cb(mainErr); + } + }); + }); + }; + return root; }); diff --git a/www/views/cashScan.html b/www/views/cashScan.html index 1d8c0a9af..602154605 100644 --- a/www/views/cashScan.html +++ b/www/views/cashScan.html @@ -14,10 +14,17 @@ BTC Wallets - {{wallet.error}} +