From bc4149561019783039ec8786c9c6e984dd0de301 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 7 Oct 2015 16:17:19 -0300 Subject: [PATCH 1/2] Set touch id to spend funds --- cordova/build.sh | 3 + public/views/preferences.html | 8 +- src/js/controllers/index.js | 16 ++- src/js/controllers/preferences.js | 79 ++++++++++--- src/js/controllers/walletHome.js | 185 ++++++++++++++++++------------ src/js/init.js | 7 +- 6 files changed, 200 insertions(+), 98 deletions(-) diff --git a/cordova/build.sh b/cordova/build.sh index 6b5f294ba..062a4f12b 100755 --- a/cordova/build.sh +++ b/cordova/build.sh @@ -129,6 +129,9 @@ if [ ! -d $PROJECT ]; then cordova plugin add org.apache.cordova.file checkOK + cordova plugin add cordova-plugin-touch-id && cordova prepare + checkOK + fi if $DBGJS diff --git a/public/views/preferences.html b/public/views/preferences.html index 0922aabbc..1221f7e11 100644 --- a/public/views/preferences.html +++ b/public/views/preferences.html @@ -6,7 +6,7 @@ -
+

    {{index.alias}} [{{index.walletName}}] settings

    @@ -40,13 +40,17 @@ Request Password for Spending Funds +
  • + Request Touch ID for Spending Funds + +
  • Hardware wallet {{preferences.externalSource}} -
  • +
  • diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index 4f3019b1a..3ef895e8a 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettext, amMoment, nodeWebkit, addonManager, feeService, isChromeApp, bwsError, txFormatService, uxLanguage, $state, glideraService, isMobile) { +angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, nodeWebkit, addonManager, feeService, isChromeApp, bwsError, txFormatService, uxLanguage, $state, glideraService, isMobile) { var self = this; self.isCordova = isCordova; self.isChromeApp = isChromeApp; @@ -1152,6 +1152,20 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.setTab(tab, reset); }); + $rootScope.$on('Local/RequestTouchid', function(event, cb) { + window.plugins.touchid.verifyFingerprint( + gettextCatalog.getString('Scan your fingerprint please'), + function(msg) { + // OK + return cb(); + }, + function(msg) { + // ERROR + return cb(gettext('Invalid Touch ID')); + } + ); + }); + $rootScope.$on('Local/ShowAlert', function(event, msg, cb) { self.showErrorPopup(msg, cb); }); diff --git a/src/js/controllers/preferences.js b/src/js/controllers/preferences.js index ea9abe04f..d5c29fadf 100644 --- a/src/js/controllers/preferences.js +++ b/src/js/controllers/preferences.js @@ -2,24 +2,34 @@ angular.module('copayApp.controllers').controller('preferencesController', function($scope, $rootScope, $filter, $timeout, $modal, $log, lodash, configService, profileService, uxLanguage) { - var config = configService.getSync(); - this.unitName = config.wallet.settings.unitName; - this.bwsurl = config.bws.url; - this.currentLanguageName = uxLanguage.getCurrentLanguageName(); - this.selectedAlternative = { - name: config.wallet.settings.alternativeName, - isoCode: config.wallet.settings.alternativeIsoCode - }; - $scope.spendUnconfirmed = config.wallet.spendUnconfirmed; - $scope.glideraEnabled = config.glidera.enabled; - $scope.glideraTestnet = config.glidera.testnet; - var fc = profileService.focusedClient; - if (fc) { - $scope.encrypt = fc.hasPrivKeyEncrypted(); - this.externalSource = fc.getPrivKeyExternalSourceName() == 'ledger' ? "Ledger" : null; - // TODO externalAccount - //this.externalIndex = fc.getExternalIndex(); - } + + this.init = function() { + var config = configService.getSync(); + this.unitName = config.wallet.settings.unitName; + this.bwsurl = config.bws.url; + this.currentLanguageName = uxLanguage.getCurrentLanguageName(); + this.selectedAlternative = { + name: config.wallet.settings.alternativeName, + isoCode: config.wallet.settings.alternativeIsoCode + }; + $scope.spendUnconfirmed = config.wallet.spendUnconfirmed; + $scope.glideraEnabled = config.glidera.enabled; + $scope.glideraTestnet = config.glidera.testnet; + var fc = profileService.focusedClient; + if (fc) { + $scope.encrypt = fc.hasPrivKeyEncrypted(); + this.externalSource = fc.getPrivKeyExternalSourceName() == 'ledger' ? "Ledger" : null; + // TODO externalAccount + //this.externalIndex = fc.getExternalIndex(); + } + + if (window.touchidAvailable) { + var walletId = fc.credentials.walletId; + this.touchidAvailable = true; + config.touchIdFor = config.touchIdFor || {}; + $scope.touchid = config.touchIdFor[walletId]; + } + }; var unwatchSpendUnconfirmed = $scope.$watch('spendUnconfirmed', function(newVal, oldVal) { if (newVal == oldVal) return; @@ -94,10 +104,43 @@ angular.module('copayApp.controllers').controller('preferencesController', }); }); + var unwatchRequestTouchid = $scope.$watch('touchid', function(newVal, oldVal) { + if (newVal == oldVal || $scope.touchidError) { + $scope.touchidError = false; + return; + } + var walletId = profileService.focusedClient.credentials.walletId; + + var opts = { + touchIdFor: {} + }; + opts.touchIdFor[walletId] = newVal; + + $rootScope.$emit('Local/RequestTouchid', function(err) { + if (err) { + $log.debug(err); + $timeout(function() { + $scope.touchidError = true; + $scope.touchid = oldVal; + }, 100); + } + else { + configService.set(opts, function(err) { + if (err) { + $log.debug(err); + $scope.touchidError = true; + $scope.touchid = oldVal; + } + }); + } + }); + }); + $scope.$on('$destroy', function() { unwatch(); unwatchSpendUnconfirmed(); unwatchGlideraEnabled(); unwatchGlideraTestnet(); + unwatchRequestTouchid(); }); }); diff --git a/src/js/controllers/walletHome.js b/src/js/controllers/walletHome.js index 3e7d253ac..34025618c 100644 --- a/src/js/controllers/walletHome.js +++ b/src/js/controllers/walletHome.js @@ -5,17 +5,19 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi var self = this; $rootScope.hideMenuBar = false; $rootScope.wpInputFocused = false; - $scope.currentSpendUnconfirmed = configService.getSync().wallet.spendUnconfirmed; + var config = configService.getSync(); + var configWallet = config.wallet; + $scope.currentSpendUnconfirmed = configWallet.spendUnconfirmed; // INIT - var config = configService.getSync().wallet.settings; - this.unitToSatoshi = config.unitToSatoshi; + var walletSettings = configWallet.settings; + this.unitToSatoshi = walletSettings.unitToSatoshi; this.satToUnit = 1 / this.unitToSatoshi; - this.unitName = config.unitName; - this.alternativeIsoCode = config.alternativeIsoCode; - this.alternativeName = config.alternativeName; + this.unitName = walletSettings.unitName; + this.alternativeIsoCode = walletSettings.alternativeIsoCode; + this.alternativeName = walletSettings.alternativeName; this.alternativeAmount = 0; - this.unitDecimals = config.unitDecimals; + this.unitDecimals = walletSettings.unitDecimals; this.isCordova = isCordova; this.addresses = []; this.isMobile = isMobile.any(); @@ -85,6 +87,16 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi $rootScope.hideMenuBar = false; }); + var requestTouchid = function(cb) { + var fc = profileService.focusedClient; + config.touchIdFor = config.touchIdFor || {}; + if (window.touchidAvailable && config.touchIdFor[fc.credentials.walletId]) { + $rootScope.$emit('Local/RequestTouchid', cb); + } else { + return cb(); + } + }; + rateService.whenAvailable(function() { self.isRateAvailable = true; $rootScope.$digest(); @@ -249,7 +261,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi }; $scope.sign = function(txp) { - var fc = profileService.focusedClient; + var fc = profileService.focusedClient; if (!fc.canSign() && !fc.isPrivKeyExternal()) return; @@ -264,45 +276,56 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi }); return; }; + self._setOngoingForSigning(); - $scope.loading = true; - $scope.error = null; + $scope.error = null; $timeout(function() { - profileService.signTxProposal(txp, function(err, txpsi) { - self.setOngoingProcess(); + requestTouchid(function(err) { if (err) { - $scope.$emit('UpdateTx'); + self.setOngoingProcess(); $scope.loading = false; - $scope.error = bwsError.msg(err, gettextCatalog.getString('Could not accept payment')); + profileService.lockFC(); + $scope.error = err; $scope.$digest(); - } else { - //if txp has required signatures then broadcast it - var txpHasRequiredSignatures = txpsi.status == 'accepted'; - if (txpHasRequiredSignatures) { - self.setOngoingProcess(gettext('Broadcasting transaction')); - $scope.loading = true; - fc.broadcastTxProposal(txpsi, function(err, txpsb, memo) { - self.setOngoingProcess(); - $scope.loading = false; - if (err) { - $scope.$emit('UpdateTx'); - $scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment')); - $scope.$digest(); - } else { - $log.debug('Transaction signed and broadcasted') - if (memo) - $log.info(memo); - - refreshUntilItChanges = true; - $modalInstance.close(txpsb); - } - }); - } else { - $scope.loading = false; - $modalInstance.close(txpsi); - } + return; } + + profileService.signTxProposal(txp, function(err, txpsi) { + self.setOngoingProcess(); + if (err) { + $scope.$emit('UpdateTx'); + $scope.loading = false; + $scope.error = bwsError.msg(err, gettextCatalog.getString('Could not accept payment')); + $scope.$digest(); + } else { + //if txp has required signatures then broadcast it + var txpHasRequiredSignatures = txpsi.status == 'accepted'; + if (txpHasRequiredSignatures) { + self.setOngoingProcess(gettext('Broadcasting transaction')); + $scope.loading = true; + fc.broadcastTxProposal(txpsi, function(err, txpsb, memo) { + self.setOngoingProcess(); + $scope.loading = false; + if (err) { + $scope.$emit('UpdateTx'); + $scope.error = bwsError.msg(err, gettextCatalog.getString('Could not broadcast payment')); + $scope.$digest(); + } else { + $log.debug('Transaction signed and broadcasted') + if (memo) + $log.info(memo); + + refreshUntilItChanges = true; + $modalInstance.close(txpsb); + } + }); + } else { + $scope.loading = false; + $modalInstance.close(txpsi); + } + } + }); }); }, 100); }; @@ -787,44 +810,56 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi } else { feeService.getCurrentFeeValue(self.currentSendFeeLevel, cb); } - }; + }; - getFee(function(err, feePerKb) { - if (err) $log.debug(err); - fc.sendTxProposal({ - toAddress: address, - amount: amount, - message: comment, - payProUrl: paypro ? paypro.url : null, - feePerKb: feePerKb, - excludeUnconfirmedUtxos: $scope.currentSpendUnconfirmed ? false : true - }, function(err, txp) { - if (err) { - self.setOngoingProcess(); - profileService.lockFC(); - return self.setSendError(err); - } + requestTouchid(function(err) { + if (err) { + profileService.lockFC(); + self.setOngoingProcess(); + self.error = err; + $timeout(function() { + $scope.$digest(); + }, 1); + return; + } - if (!fc.canSign() && !fc.isPrivKeyExternal()) { - $log.info('No signing proposal: No private key') - self.setOngoingProcess(); - self.resetForm(); - txStatus.notify(txp, function() { - return $scope.$emit('Local/TxProposalAction'); - }); - return; - } - - self.signAndBroadcast(txp, function(err) { - self.setOngoingProcess(); - self.resetForm(); + getFee(function(err, feePerKb) { + if (err) $log.debug(err); + fc.sendTxProposal({ + toAddress: address, + amount: amount, + message: comment, + payProUrl: paypro ? paypro.url : null, + feePerKb: feePerKb, + excludeUnconfirmedUtxos: $scope.currentSpendUnconfirmed ? false : true + }, function(err, txp) { if (err) { - self.error = err.message ? err.message : gettext('The payment was created but could not be completed. Please try again from home screen'); - $scope.$emit('Local/TxProposalAction'); - $timeout(function() { - $scope.$digest(); - }, 1); + self.setOngoingProcess(); + profileService.lockFC(); + return self.setSendError(err); } + + if (!fc.canSign() && !fc.isPrivKeyExternal()) { + $log.info('No signing proposal: No private key') + self.setOngoingProcess(); + self.resetForm(); + txStatus.notify(txp, function() { + return $scope.$emit('Local/TxProposalAction'); + }); + return; + } + + self.signAndBroadcast(txp, function(err) { + self.setOngoingProcess(); + self.resetForm(); + if (err) { + self.error = err.message ? err.message : gettext('The payment was created but could not be completed. Please try again from home screen'); + $scope.$emit('Local/TxProposalAction'); + $timeout(function() { + $scope.$digest(); + }, 1); + } + }); }); }); }); @@ -1122,7 +1157,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi var fc = profileService.focusedClient; var ModalInstanceCtrl = function($scope, $modalInstance) { $scope.btx = btx; - $scope.settings = config; + $scope.settings = walletSettings; $scope.color = fc.backgroundColor; $scope.copayerId = fc.credentials.copayerId; $scope.isShared = fc.credentials.n > 1; diff --git a/src/js/init.js b/src/js/init.js index b5c5d8066..75e2d5766 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -57,8 +57,6 @@ angular.element(document).ready(function() { window.location = '#/preferences'; }, false); - - setTimeout(function() { navigator.splashscreen.hide(); }, 2000); @@ -67,6 +65,11 @@ angular.element(document).ready(function() { window.plugins.webintent.onNewIntent(handleBitcoinURI); window.handleOpenURL = handleBitcoinURI; + window.plugins.touchid.isAvailable( + function(msg) { window.touchidAvailable = true; }, // success handler: TouchID available + function(msg) { window.touchidAvailable = false; } // error handler: no TouchID available + ); + startAngular(); }, false); } else { From 92186fcfdcb928111afbb93c20a1c299e3a35fdb Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 8 Oct 2015 11:48:40 -0300 Subject: [PATCH 2/2] Fix plugin url for cordova 4.3.0 --- cordova/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cordova/build.sh b/cordova/build.sh index 062a4f12b..1bf496ce1 100755 --- a/cordova/build.sh +++ b/cordova/build.sh @@ -129,7 +129,7 @@ if [ ! -d $PROJECT ]; then cordova plugin add org.apache.cordova.file checkOK - cordova plugin add cordova-plugin-touch-id && cordova prepare + cordova plugin add https://github.com/EddyVerbruggen/cordova-plugin-touch-id && cordova prepare checkOK fi