From d1c1638b1477da3d5a2afd7b3dcca7c83e8e33ee Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 4 Nov 2015 00:53:26 -0300 Subject: [PATCH 01/21] WIP trezor 48 --- public/views/create.html | 22 ++++++----- src/js/controllers/create.js | 45 ++++++++++++++++++++-- src/js/controllers/import.js | 10 +++-- src/js/controllers/join.js | 4 +- src/js/services/hwWallet.js | 38 +++++++++++++++++++ src/js/services/ledger.js | 53 +++++++++++--------------- src/js/services/trezor.js | 73 ++++++++++-------------------------- 7 files changed, 141 insertions(+), 104 deletions(-) create mode 100644 src/js/services/hwWallet.js diff --git a/public/views/create.html b/public/views/create.html index a47499fce..0bf4b6731 100644 --- a/public/views/create.html +++ b/public/views/create.html @@ -99,8 +99,17 @@
-
+
+ +
+ +
- - -
-
+ +
+ +
+ + +
- -
-
-
+ +
+ +
@@ -168,8 +162,7 @@
- - +
-
-
- -
-
- -
+
+ +
+
+
@@ -98,27 +96,25 @@
+
- -
- -
+
+ +
+
+ +
-
- -
- -
Multiple wallets accounts are supported on the device simultaneously. Select which account should be used for this wallet
- - - - +
+ + + + WARNING: Passphrase cannot be recovered. Be sure to write it down. The wallet can not be restored without the passphrase. + + +
+
+ +
-
+
+ +
-
- - - - WARNING: Passphrase cannot be recovered. Be sure to write it down. The wallet can not be restored without the passphrase. - - -
-
+
+ +
+ +
+ +
+
+
- - + + +
diff --git a/public/views/import.html b/public/views/import.html index 4a59ba493..070aa4764 100644 --- a/public/views/import.html +++ b/public/views/import.html @@ -30,8 +30,6 @@ - -
@@ -46,21 +44,21 @@
-
-
-
-
- - {{import.error|translate}} - -
+
+
+ +
+ + {{import.error|translate}} + +
-
- - -
+
+ + +
- - -
+ +
+
+
+
+
+
+ + {{import.error|translate}} + +
- -
- + +
+
+
+ + {{import.error|translate}} + +
+ +
+
+
+ No harware wallets supported on this device +
+
+
+ +
+ +
+ +
+ +
+ + +
Multiple wallets accounts are supported on the device simultaneously. Select which account should be imported
@@ -152,100 +222,16 @@
- - -
-

Have a Backup from Copay v0.9?

- -
- -
-
- -
-
-
- - {{import.error|translate}} - -
- -
-
- - - - -
-
- - -
-
-
-
- -
-
- - -
-
- - -
-
- - - -
-
- -
+
+
+
- - +
diff --git a/public/views/includes/walletInfo.html b/public/views/includes/walletInfo.html index e1439551b..9588159de 100644 --- a/public/views/includes/walletInfo.html +++ b/public/views/includes/walletInfo.html @@ -3,4 +3,7 @@
External Private Key: {{index.externalSource}} + (Account: + #{{index.account || 0}}) +
diff --git a/public/views/join.html b/public/views/join.html index d82c6b3ba..3ec49f38a 100644 --- a/public/views/join.html +++ b/public/views/join.html @@ -32,118 +32,123 @@
-
-
+
+
+
+
+ + {{join.error|translate}} + +
-
- - {{join.error|translate}} - -
- - +
+
-
- - - - Wallet Invitation is not valid! - - +
+ + + + Wallet Invitation is not valid! + + +
+ +
+ +
+
+
-
- -
- -
-
- - +
+ - Show advanced options - Hide advanced options - - + Show advanced options + Hide advanced options + + -
-
+
- +
+
+
+ +
- +
+ +
- - - +
+ + + + WARNING: Passphrase cannot be recovered. Be sure to write it down. The wallet can not be restored without the passphrase. + + +
- - +
+ +
- -
-
+
+ +
-
- - - - WARNING: Passphrase cannot be recovered. Be sure to write it down. The wallet can not be restored without the passphrase. - - -
+
+ +
+
+
- - -
-
+ +
+
+
diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index 6fe50212d..3e343eb67 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -48,9 +48,8 @@ angular.module('copayApp.controllers').controller('createController', label: gettext('Specify Seed...'), }]; $scope.seedSource = self.seedOptions[0]; - - // TODO - // if (!isChromeApp) return; +// TODO +// if (!isChromeApp) return; if (n > 1) self.seedOptions.push({ @@ -187,6 +186,7 @@ angular.module('copayApp.controllers').controller('createController', $scope.$on("$destroy", function() { $rootScope.hideWalletNavigation = false; }); + updateSeedSourceSelect(1); - self.seedSourceId = 'new' + self.setSeedSource('new'); }); diff --git a/src/js/controllers/import.js b/src/js/controllers/import.js index ef4bfb4a2..92958df0e 100644 --- a/src/js/controllers/import.js +++ b/src/js/controllers/import.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('importController', - function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor) { + function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, isChromeApp) { var self = this; var reader = new FileReader(); @@ -15,6 +15,25 @@ angular.module('copayApp.controllers').controller('importController', }, 100); }); + var updateSeedSourceSelect = function() { + self.seedOptions = []; +// TODO +// if (!isChromeApp) return; + + self.seedOptions.push({ + id: 'ledger', + label: gettext('Ledger Hardware Wallet'), + }); + + self.seedOptions.push({ + id: 'trezor', + label: gettext('Trezor Hardware Wallet'), + }); + $scope.seedSource = self.seedOptions[0]; + }; + + + this.setType = function(type) { $scope.type = type; this.error = null; @@ -178,19 +197,8 @@ angular.module('copayApp.controllers').controller('importController', _importMnemonic(words, opts); }; - this.importTrezor = function(form) { + this.importTrezor = function(account, isMultisig) { var self = this; - if (form.$invalid) { - this.error = gettext('There is an error in the form'); - $timeout(function() { - $scope.$apply(); - }); - return; - } - self.hwWallet = 'Trezor'; - var account = form.account.$modelValue; - var isMultisig = form.isMultisig.$modelValue; - trezor.getInfoForNewWallet(isMultisig, account, function(err, lopts) { self.hwWallet = false; if (err) { @@ -219,8 +227,7 @@ angular.module('copayApp.controllers').controller('importController', }, 100); }; - this.importLedger = function(form) { - var self = this; + this.importHW = function(form) { if (form.$invalid) { this.error = gettext('There is an error in the form'); $timeout(function() { @@ -228,8 +235,40 @@ angular.module('copayApp.controllers').controller('importController', }); return; } - self.hwWallet = 'Ledger'; - var account = form.account.$modelValue; + + var account = $scope.account; + var isMultisig = form.isMultisig.$modelValue; + + switch (self.seedSourceId) { + case ('ledger'): + self.hwWallet = 'Ledger'; + self.importLedger(account); + break; + case ('trezor'): + self.hwWallet = 'Trezor'; + self.importTrezor(account, isMultisig); + break; + default: + throw ('Error: bad source id'); + }; + }; + + this.setSeedSource = function() { + if (!$scope.seedSource) return; + self.seedSourceId = $scope.seedSource.id; + + if (self.seedSourceId == 'ledger') + self.accountValues = lodash.range(0, 99); + else + self.accountValues = lodash.range(1, 100); + + $timeout(function() { + $rootScope.$apply(); + }); + }; + + this.importLedger = function(account) { + var self = this; ledger.getInfoForNewWallet(true, account, function(err, lopts) { self.hwWallet = false; if (err) { @@ -257,4 +296,6 @@ angular.module('copayApp.controllers').controller('importController', }, 100); }; + updateSeedSourceSelect(); + self.setSeedSource('new'); }); diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index df5c45a21..2e7979407 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -107,6 +107,11 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.canSign = fc.canSign(); self.isPrivKeyExternal = fc.isPrivKeyExternal(); self.externalSource = fc.getPrivKeyExternalSourceName(); + self.account = fc.credentials.account; + + if (self.externalSource == 'trezor') + self.account++; + self.txps = []; self.copayers = []; self.updateColor(); diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js index 5a4ffe4bc..40bc9806f 100644 --- a/src/js/controllers/join.js +++ b/src/js/controllers/join.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('joinController', - function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor) { + function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, isChromeApp) { var self = this; var defaults = configService.getDefaults(); @@ -13,6 +13,44 @@ angular.module('copayApp.controllers').controller('joinController', $scope.joinForm.secret.$render(); }; + + var updateSeedSourceSelect = function() { + self.seedOptions = [{ + id: 'new', + label: gettext('New Random Seed'), + }, { + id: 'set', + label: gettext('Specify Seed...'), + }]; + $scope.seedSource = self.seedOptions[0]; + +// TODO +// if (!isChromeApp) return; + + self.seedOptions.push({ + id: 'ledger', + label: gettext('Ledger Hardware Wallet'), + }); + + self.seedOptions.push({ + id: 'trezor', + label: gettext('Trezor Hardware Wallet'), + }); + }; + + this.setSeedSource = function(src) { + self.seedSourceId = $scope.seedSource.id; + + if (self.seedSourceId == 'ledger') + self.accountValues = lodash.range(0, 99); + else + self.accountValues = lodash.range(1, 100); + + $timeout(function() { + $rootScope.$apply(); + }); + }; + this.join = function(form) { if (form && form.$invalid) { self.error = gettext('Please enter the required fields'); @@ -26,7 +64,7 @@ angular.module('copayApp.controllers').controller('joinController', bwsurl: $scope.bwsurl } - var setSeed = form.setSeed.$modelValue; + var setSeed = self.seedSourceId =='set'; if (setSeed) { var words = form.privateKey.$modelValue; if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) { @@ -44,10 +82,14 @@ angular.module('copayApp.controllers').controller('joinController', return; } - if (form.hwLedger.$modelValue || form.hwTrezor.$modelValue) { - self.hwWallet = form.hwLedger.$modelValue ? 'Ledger' : 'TREZOR'; - var src = form.hwLedger.$modelValue ? ledger : trezor; - var account = form.account.$modelValue; + if (self.seedSourceId == 'ledger' || self.seedSourceId == 'trezor') { + var account = $scope.account; + if (!account) { + this.error = gettext('Please select account'); + return; + } + self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor'; + var src = self.seedSourceId == 'ledger' ? ledger : trezor; src.getInfoForNewWallet(true, account, function(err, lopts) { self.hwWallet = false; @@ -82,4 +124,7 @@ angular.module('copayApp.controllers').controller('joinController', }); }, 100); }; + + updateSeedSourceSelect(); + self.setSeedSource('new'); }); diff --git a/src/js/services/ledger.js b/src/js/services/ledger.js index 97e61b09a..7183fdfe7 100644 --- a/src/js/services/ledger.js +++ b/src/js/services/ledger.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.services') - .factory('ledger', function($log, bwcService, gettext) { + .factory('ledger', function($log, bwcService, gettext, hwWallet) { var root = {}; var LEDGER_CHROME_ID = "kkdpmhnladdopljabkgpacgpliggeeaf"; From de8551812b3d9bf9167b8f9578d85b20ecc2b16a Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 4 Nov 2015 15:45:53 -0300 Subject: [PATCH 07/21] addtrezor-url --- src/js/trezor-url.js | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/js/trezor-url.js diff --git a/src/js/trezor-url.js b/src/js/trezor-url.js new file mode 100644 index 000000000..7b2255280 --- /dev/null +++ b/src/js/trezor-url.js @@ -0,0 +1,2 @@ +window.TREZOR_CHROME_URL = './bower_components/trezor-connect/chrome/wrapper.html'; + From 1892d16b922bb2bea764c823ec4a1fa150c9e25c Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 4 Nov 2015 17:50:05 -0300 Subject: [PATCH 08/21] support import --- public/views/create.html | 17 ++++++++++++++++- public/views/import.html | 8 ++++++++ src/js/controllers/create.js | 16 +++++++--------- src/js/controllers/import.js | 12 +++++------- src/js/controllers/join.js | 10 ++-------- src/js/services/hwWallet.js | 19 ++++++++++++++++--- src/js/services/ledger.js | 7 ------- src/js/services/profileService.js | 10 +++++++++- 8 files changed, 63 insertions(+), 36 deletions(-) diff --git a/public/views/create.html b/public/views/create.html index ddfc90ec4..d666209d9 100644 --- a/public/views/create.html +++ b/public/views/create.html @@ -160,7 +160,22 @@ -
+
+ +
Multiple accounts can be derived from the same seed. Specify which account to use
+
+ +
+ +
+ +
+
+ +
Multiple accounts can be derived from the same seed. Specify which account to import
+
+
+ +
+ +
Multiple accounts can be derived from the same seed. Specify which account to use
+
+ +
diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index 8949dcb77..4c9fd49ca 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -123,6 +123,7 @@ angular.module('copayApp.controllers').controller('createController', this.error = gettext('Please select account'); return; } + opts.account = account; self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor'; var src = self.seedSourceId == 'ledger' ? ledger : trezor; diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js index 66c8cb331..8fa30f43a 100644 --- a/src/js/controllers/join.js +++ b/src/js/controllers/join.js @@ -6,6 +6,8 @@ angular.module('copayApp.controllers').controller('joinController', var self = this; var defaults = configService.getDefaults(); $scope.bwsurl = defaults.bws.url; + self.accountValuesForSeed = lodash.range(0, 100); + $scope.accountForSeed = 0; this.onQrCodeScanned = function(data) { $scope.secret = data; @@ -55,7 +57,8 @@ angular.module('copayApp.controllers').controller('joinController', var opts = { secret: form.secret.$modelValue, myName: form.myName.$modelValue, - bwsurl: $scope.bwsurl + bwsurl: $scope.bwsurl, + account: $scope.accountForSeed || 0, } var setSeed = self.seedSourceId =='set'; @@ -82,6 +85,7 @@ angular.module('copayApp.controllers').controller('joinController', this.error = gettext('Please select account'); return; } + opts.account = account; self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor'; var src = self.seedSourceId == 'ledger' ? ledger : trezor; diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index 7d5cfc2d9..8382316e6 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -186,11 +186,10 @@ angular.module('copayApp.services') // 1) { - walletClient.credentials.derivationStrategy == + //walletClient.credentials.derivationStrategy == } - // Nasty> - } + } catch (ex) { $log.info(ex); return cb(gettext('Could not create: Invalid wallet seed')); From 5abf35c68cff140f266011c2293802eb30be62c7 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 5 Nov 2015 16:00:38 -0300 Subject: [PATCH 10/21] adds trezor support to isDevel --- src/js/controllers/create.js | 16 ++++++++-------- src/js/controllers/import.js | 25 ++++++++++++++----------- src/js/controllers/join.js | 24 ++++++++++++++---------- src/js/services/isDevel.js | 5 +++++ src/js/services/profileService.js | 19 +++++++++++-------- 5 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 src/js/services/isDevel.js diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index 4c9fd49ca..44a11f67b 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('createController', - function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova, gettext, ledger, trezor, isMobile, isChromeApp) { + function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova, gettext, ledger, trezor, isMobile, isChromeApp, isDevel) { var self = this; var defaults = configService.getDefaults(); @@ -50,19 +50,19 @@ angular.module('copayApp.controllers').controller('createController', label: gettext('Specify Seed...'), }]; $scope.seedSource = self.seedOptions[0]; - if (!isChromeApp) return; - if (n > 1) + if (n > 1 && isChromeApp) self.seedOptions.push({ id: 'ledger', label: gettext('Ledger Hardware Wallet'), }); - self.seedOptions.push({ - id: 'trezor', - label: gettext('Trezor Hardware Wallet'), - }); - + if (isChromeApp || isDevel) { + self.seedOptions.push({ + id: 'trezor', + label: gettext('Trezor Hardware Wallet'), + }); + } }; this.TCValues = lodash.range(2, defaults.limits.totalCopayers + 1); diff --git a/src/js/controllers/import.js b/src/js/controllers/import.js index 540e83fb9..8eeec31c4 100644 --- a/src/js/controllers/import.js +++ b/src/js/controllers/import.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('importController', - function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, isChromeApp) { + function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, isChromeApp, isDevel) { var self = this; var reader = new FileReader(); @@ -19,18 +19,21 @@ angular.module('copayApp.controllers').controller('importController', var updateSeedSourceSelect = function() { self.seedOptions = []; - if (!isChromeApp) return; - self.seedOptions.push({ - id: 'ledger', - label: gettext('Ledger Hardware Wallet'), - }); + if (isChromeApp) { + self.seedOptions.push({ + id: 'ledger', + label: gettext('Ledger Hardware Wallet'), + }); + } - self.seedOptions.push({ - id: 'trezor', - label: gettext('Trezor Hardware Wallet'), - }); - $scope.seedSource = self.seedOptions[0]; + if (isChromeApp || isDevel) { + self.seedOptions.push({ + id: 'trezor', + label: gettext('Trezor Hardware Wallet'), + }); + $scope.seedSource = self.seedOptions[0]; + } }; diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js index 8fa30f43a..376a60100 100644 --- a/src/js/controllers/join.js +++ b/src/js/controllers/join.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('joinController', - function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, isChromeApp) { + function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, isChromeApp, isDevel) { var self = this; var defaults = configService.getDefaults(); @@ -25,17 +25,21 @@ angular.module('copayApp.controllers').controller('joinController', label: gettext('Specify Seed...'), }]; $scope.seedSource = self.seedOptions[0]; - if (!isChromeApp) return; - self.seedOptions.push({ - id: 'ledger', - label: gettext('Ledger Hardware Wallet'), - }); - self.seedOptions.push({ - id: 'trezor', - label: gettext('Trezor Hardware Wallet'), - }); + if (isChromeApp) { + self.seedOptions.push({ + id: 'ledger', + label: gettext('Ledger Hardware Wallet'), + }); + } + + if (isChromeApp || isDevel) { + self.seedOptions.push({ + id: 'trezor', + label: gettext('Trezor Hardware Wallet'), + }); + } }; this.setSeedSource = function(src) { diff --git a/src/js/services/isDevel.js b/src/js/services/isDevel.js new file mode 100644 index 000000000..f96ea53e4 --- /dev/null +++ b/src/js/services/isDevel.js @@ -0,0 +1,5 @@ +'use strict'; + +angular.module('copayApp.services').factory('isDevel', function(nodeWebkit, isChromeApp, isMobile) { + return !isMobile.any() && !isChromeApp && !nodeWebkit.isDefined(); +}); diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index 8382316e6..2def22e4b 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -175,6 +175,13 @@ angular.module('copayApp.services') var walletClient = bwcService.getClient(); var network = opts.networkName || 'livenet'; + // TODO refactor this and use bwc contants? + var derivationStrategy = 'BIP44'; + if (opts.fromHardware && opts.n > 1) { + derivationStrategy = 'BIP48'; + } + + if (opts.mnemonic) { try { opts.mnemonic = root._normalizeMnemonic(opts.mnemonic); @@ -182,14 +189,9 @@ angular.module('copayApp.services') network: network, passphrase: opts.passphrase, account: opts.account || 0, + derivationStrategy: derivationStrategy, }); - // 1) { - //walletClient.credentials.derivationStrategy == - } - // Nasty> - } catch (ex) { $log.info(ex); return cb(gettext('Could not create: Invalid wallet seed')); @@ -204,7 +206,8 @@ angular.module('copayApp.services') } else if (opts.extendedPublicKey) { try { walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, { - account: opts.account || 0, + account: opts.account || 0, + derivationStrategy: derivationStrategy, }); } catch (ex) { $log.warn("Creating wallet from Extended Public Key Arg:", ex, opts); @@ -368,7 +371,7 @@ angular.module('copayApp.services') root.setWalletClients(); root.setAndStoreFocus(walletId, function() { - storageService.storeProfile(root.profile, function(err){ + storageService.storeProfile(root.profile, function(err) { return cb(err, walletId); }); }); From 791efca7147504750650e3b71e7c40d8afcb485a Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 5 Nov 2015 19:46:32 -0300 Subject: [PATCH 11/21] fix account indexes, add base path in settings --- baseDerivation.md | 26 ++++++++++++++ public/views/create.html | 23 +++---------- public/views/import.html | 34 +++++++----------- public/views/join.html | 9 ++--- src/js/controllers/create.js | 35 +++++++++++++------ src/js/controllers/import.js | 32 ++++++++++++----- src/js/controllers/join.js | 23 +++++++++---- src/js/routes.js | 5 +-- src/js/services/derivationPathHelper.js | 46 +++++++++++++++++++++++++ src/js/services/hwWallet.js | 2 +- src/js/services/trezor.js | 1 - 11 files changed, 162 insertions(+), 74 deletions(-) create mode 100644 baseDerivation.md create mode 100644 src/js/services/derivationPathHelper.js diff --git a/baseDerivation.md b/baseDerivation.md new file mode 100644 index 000000000..164a708fe --- /dev/null +++ b/baseDerivation.md @@ -0,0 +1,26 @@ + +Copay accepts three base derivation paths: + + * m/44' + * m/48' (only used for MULTISIGNATURE, HARDWARE Wallets) + * m/45' (deprecated and it is only supported for old wallets) + +Both m/44 and m/48 follow the BIP44 standard: + +m/XX'/'/ + +Supported cointypes are: 0: Livenet, and 1: Testnet + +If you need to import a wallet from a mnemonic using an account different +from the default (0), use, for example: + + m/44'/0'/11' + +to import account 11. + +In case you have a multisignature wallet originally created from a hardware device, and you had loose access to the device, you will need to enter the 24 mnemonic backup (from the device) and a path like: + + + m/48'/0'/8' + +for a multisignature wallet, account 8. diff --git a/public/views/create.html b/public/views/create.html index d666209d9..79f17bf97 100644 --- a/public/views/create.html +++ b/public/views/create.html @@ -116,12 +116,9 @@
- -
@@ -161,21 +158,11 @@
- -
Multiple accounts can be derived from the same seed. Specify which account to use
-
- -
-
- -
+
- - -
- -
Multiple accounts can be derived from the same seed. Specify which account to import
-
+
+ +
+
+
+ + +
+
-
- - -
Multiple wallets accounts are supported on the device simultaneously. Select which account should be imported
-
-
-
-
- -
diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index 44a11f67b..0a19e1726 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -1,11 +1,12 @@ 'use strict'; angular.module('copayApp.controllers').controller('createController', - function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova, gettext, ledger, trezor, isMobile, isChromeApp, isDevel) { + function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova, gettext, ledger, trezor, isMobile, isChromeApp, isDevel, derivationPathHelper) { var self = this; var defaults = configService.getDefaults(); this.isWindowsPhoneApp = isMobile.Windows() && isCordova; + $scope.account = 1; /* For compressed keys, m*73 + n*34 <= 496 */ var COPAYER_PAIR_LIMITS = { @@ -25,8 +26,7 @@ angular.module('copayApp.controllers').controller('createController', var defaults = configService.getDefaults(); $scope.bwsurl = defaults.bws.url; - self.accountValuesForSeed = lodash.range(0, 100); - $scope.accountForSeed = 0; + $scope.derivationPath = derivationPathHelper.default; // ng-repeat defined number of times instead of repeating over array? this.getNumber = function(num) { @@ -77,7 +77,6 @@ angular.module('copayApp.controllers').controller('createController', this.setSeedSource = function(src) { self.seedSourceId = $scope.seedSource.id; - self.accountValues = lodash.range(1, 100); $timeout(function() { $rootScope.$apply(); @@ -89,6 +88,7 @@ angular.module('copayApp.controllers').controller('createController', this.error = gettext('Please enter the required fields'); return; } + var opts = { m: $scope.requiredCopayers, n: $scope.totalCopayers, @@ -96,18 +96,29 @@ angular.module('copayApp.controllers').controller('createController', myName: $scope.totalCopayers > 1 ? form.myName.$modelValue : null, networkName: form.isTestnet.$modelValue ? 'testnet' : 'livenet', bwsurl: $scope.bwsurl, - account: $scope.accountForSeed || 0, use48: $scope.fromHardware, }; - var setSeed = self.seedSourceId =='set'; + var setSeed = self.seedSourceId == 'set'; if (setSeed) { - var words = form.privateKey.$modelValue; + + var words = form.privateKey.$modelValue || ''; if (words.indexOf(' ') == -1 && words.indexOf('prv') == 1 && words.length > 108) { opts.extendedPrivateKey = words; } else { opts.mnemonic = words; } opts.passphrase = form.passphrase.$modelValue; + + var pathData = derivationPathHelper.parse($scope.derivationPath); + if (!pathData) { + this.error = gettext('Invalid derivation path'); + return; + } + + opts.account = pathData.account; + opts.networkName = pathData.networkName; + opts.derivationStrategy = pathData.derivationStrategy; + } else { opts.passphrase = form.createPassphrase.$modelValue; } @@ -119,11 +130,15 @@ angular.module('copayApp.controllers').controller('createController', if (self.seedSourceId == 'ledger' || self.seedSourceId == 'trezor') { var account = $scope.account; - if (!account) { - this.error = gettext('Please select account'); + if (!account || account < 1) { + this.error = gettext('Invalid account number'); return; } - opts.account = account; + + if ( self.seedSourceId == 'trezor') + account = account - 1; + + opts.account = account; self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor'; var src = self.seedSourceId == 'ledger' ? ledger : trezor; diff --git a/src/js/controllers/import.js b/src/js/controllers/import.js index 8eeec31c4..f89c3dd21 100644 --- a/src/js/controllers/import.js +++ b/src/js/controllers/import.js @@ -1,14 +1,14 @@ 'use strict'; angular.module('copayApp.controllers').controller('importController', - function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, isChromeApp, isDevel) { + function($scope, $rootScope, $location, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, isChromeApp, isDevel, derivationPathHelper) { var self = this; var reader = new FileReader(); var defaults = configService.getDefaults(); $scope.bwsurl = defaults.bws.url; - $scope.accountForSeed = 0; - self.accountValuesForSeed = lodash.range(0, 100); + $scope.derivationPath = derivationPathHelper.default; + $scope.account = 1; window.ignoreMobilePause = true; $scope.$on('$destroy', function() { @@ -196,8 +196,16 @@ angular.module('copayApp.controllers').controller('importController', } opts.passphrase = form.passphrase.$modelValue || null; - opts.networkName = form.isTestnet.$modelValue ? 'testnet' : 'livenet'; - opts.account = $scope.accountForSeed; + + var pathData = derivationPathHelper.parse($scope.derivationPath); + if (!pathData) { + this.error = gettext('Invalid derivation path'); + return; + } + opts.account = pathData.account; + opts.networkName = pathData.networkName; + opts.derivationStrategy = pathData.derivationStrategy; + _importMnemonic(words, opts); }; @@ -233,15 +241,24 @@ angular.module('copayApp.controllers').controller('importController', }; this.importHW = function(form) { - if (form.$invalid) { + if (form.$invalid || $scope.account < 0 ) { this.error = gettext('There is an error in the form'); $timeout(function() { $scope.$apply(); }); return; } + this.error = ''; - var account = $scope.account; + var account = + $scope.account; + + if (self.seedSourceId == 'trezor') { + if ( account < 1) { + this.error = gettext('Invalid account number'); + return; + } + account = account - 1; + } var isMultisig = form.isMultisig.$modelValue; switch (self.seedSourceId) { @@ -261,7 +278,6 @@ angular.module('copayApp.controllers').controller('importController', this.setSeedSource = function() { if (!$scope.seedSource) return; self.seedSourceId = $scope.seedSource.id; - self.accountValues = lodash.range(1, 100); $timeout(function() { $rootScope.$apply(); diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js index 376a60100..31ae0158d 100644 --- a/src/js/controllers/join.js +++ b/src/js/controllers/join.js @@ -1,13 +1,12 @@ 'use strict'; angular.module('copayApp.controllers').controller('joinController', - function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, isChromeApp, isDevel) { + function($scope, $rootScope, $timeout, go, notification, profileService, configService, isCordova, storageService, applicationService, $modal, gettext, lodash, ledger, trezor, isChromeApp, isDevel,derivationPathHelper) { var self = this; var defaults = configService.getDefaults(); $scope.bwsurl = defaults.bws.url; - self.accountValuesForSeed = lodash.range(0, 100); - $scope.accountForSeed = 0; + $scope.derivationPath = derivationPathHelper.default; this.onQrCodeScanned = function(data) { $scope.secret = data; @@ -62,7 +61,6 @@ angular.module('copayApp.controllers').controller('joinController', secret: form.secret.$modelValue, myName: form.myName.$modelValue, bwsurl: $scope.bwsurl, - account: $scope.accountForSeed || 0, } var setSeed = self.seedSourceId =='set'; @@ -74,6 +72,15 @@ angular.module('copayApp.controllers').controller('joinController', opts.mnemonic = words; } opts.passphrase = form.passphrase.$modelValue; + + var pathData = derivationPathHelper.parse($scope.derivationPath); + if (!pathData) { + this.error = gettext('Invalid derivation path'); + return; + } + opts.account = pathData.account; + opts.networkName = pathData.networkName; + opts.derivationStrategy = pathData.derivationStrategy; } else { opts.passphrase = form.createPassphrase.$modelValue; } @@ -85,10 +92,14 @@ angular.module('copayApp.controllers').controller('joinController', if (self.seedSourceId == 'ledger' || self.seedSourceId == 'trezor') { var account = $scope.account; - if (!account) { - this.error = gettext('Please select account'); + if (!account || account < 1) { + this.error = gettext('Invalid account number'); return; } + + if ( self.seedSourceId == 'trezor') + account = account - 1; + opts.account = account; self.hwWallet = self.seedSourceId == 'ledger' ? 'Ledger' : 'Trezor'; var src = self.seedSourceId == 'ledger' ? ledger : trezor; diff --git a/src/js/routes.js b/src/js/routes.js index 1533ba74a..326527899 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -21,13 +21,14 @@ angular $logProvider.debugEnabled(true); $provide.decorator('$log', ['$delegate', - function($delegate) { + function($delegate, isDevel) { var historicLog = historicLogProvider.$get(); ['debug', 'info', 'warn', 'error', 'log'].forEach(function(level) { + if (isDevel && level == 'error') return; + var orig = $delegate[level]; $delegate[level] = function() { - if (level == 'error') console.log(arguments); diff --git a/src/js/services/derivationPathHelper.js b/src/js/services/derivationPathHelper.js new file mode 100644 index 000000000..04588692d --- /dev/null +++ b/src/js/services/derivationPathHelper.js @@ -0,0 +1,46 @@ +'use strict'; + +angular.module('copayApp.services').factory('derivationPathHelper', function(lodash) { + var root = {}; + + root.default = "m/44'/0'/0'" + root.parse = function(str) { + var arr = str.split('/'); + + var ret = {}; + + if (arr[0] != 'm') + return false; + + switch (arr[1]) { + case "44'": + ret.derivationStrategy = 'BIP44'; + break; + case "48'": + ret.derivationStrategy = 'BIP48'; + break; + default: + return false; + }; + + switch (arr[2]) { + case "0'": + ret.networkName = 'livenet'; + break; + case "1'": + ret.networkName = 'testnet'; + break; + default: + return false; + }; + + var match = arr[3].match(/(\d+)'/); + if (!match) + return false; + ret.account = + match[1] + + return ret; + }; + + return root; +}); diff --git a/src/js/services/hwWallet.js b/src/js/services/hwWallet.js index 7e696046d..cf81f71f1 100644 --- a/src/js/services/hwWallet.js +++ b/src/js/services/hwWallet.js @@ -13,7 +13,7 @@ angular.module('copayApp.services') root._err = function(data) { var msg = 'Hardware Wallet Error: ' + (data.error || data.message || 'unknown'); $log.warn(msg); - return JSON.parse(JSON.stringify(msg)); + return msg; }; root.getAddressPath = function(isMultisig, account) { diff --git a/src/js/services/trezor.js b/src/js/services/trezor.js index 295b0f05f..4a16df8de 100644 --- a/src/js/services/trezor.js +++ b/src/js/services/trezor.js @@ -24,7 +24,6 @@ angular.module('copayApp.services') root.getInfoForNewWallet = function(isMultisig, account, callback) { - account = account - 1; var opts = {}; root.getEntropySource(isMultisig, account, function(err, data) { if (err) return callback(err); From b574b6909bd77d26e96d27c8be6146cacd8233e3 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 5 Nov 2015 19:49:36 -0300 Subject: [PATCH 12/21] update doc --- baseDerivation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/baseDerivation.md b/baseDerivation.md index 164a708fe..e238e4f4c 100644 --- a/baseDerivation.md +++ b/baseDerivation.md @@ -24,3 +24,5 @@ In case you have a multisignature wallet originally created from a hardware devi m/48'/0'/8' for a multisignature wallet, account 8. + +Finally, note that TREZOR use 1-based account numbers, so if your are trying for example to recover TREZOR multisig account #8, you should enter `m/48'/0'/7'`. From 784031a7c0b8924463f877560956475988bb817e Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 6 Nov 2015 13:06:08 -0300 Subject: [PATCH 13/21] fix TX history delay --- public/views/walletHome.html | 32 ++++++--- src/js/controllers/index.js | 124 +++++++++++++++++++++-------------- 2 files changed, 96 insertions(+), 60 deletions(-) diff --git a/public/views/walletHome.html b/public/views/walletHome.html index 353f5198f..ebe25ccb1 100644 --- a/public/views/walletHome.html +++ b/public/views/walletHome.html @@ -467,18 +467,16 @@
-

+

Initial transaction history synchronization can take some minutes for wallets with many transactions.
Please stand by. -

-
-
+
+ {{index.txProgress}} Transactions
+ Downloaded +
+
+ + +
+
+ +
+ +
diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index 2e7979407..61eeb9de7 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -7,7 +7,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.isChromeApp = isChromeApp; self.isSafari = isMobile.Safari(); self.onGoingProcess = {}; - self.limitHistory = 6; + self.historyShowLimit = 10; function strip(number) { return (parseFloat(number.toPrecision(12))); @@ -83,7 +83,9 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.currentFeeLevel = null; self.notAuthorized = false; self.txHistory = []; - self.txHistoryUnique = {}; + self.completeHistory = []; + self.txProgress = 0; + self.historyShowShowAll = false; self.balanceByAddress = null; self.pendingTxProposalsCountForUs = null; self.setSpendUnconfirmed(); @@ -490,12 +492,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r var SAFE_CONFIRMATIONS = 6; - self.setTxHistory = function(txs) { + self.processNewTxs = function(txs) { var config = configService.getSync().wallet.settings; var now = Math.floor(Date.now() / 1000); - self.txHistoryUnique = {}; - + var txHistoryUnique = {}; + var ret = []; self.hasUnsafeConfirmed = false; + lodash.each(txs, function(tx) { tx = txFormatService.processTx(tx); @@ -510,13 +513,15 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.hasUnsafeConfirmed = true; } - if (!self.txHistoryUnique[tx.txid]) { - self.txHistory.push(tx); - self.txHistoryUnique[tx.txid] = true; + if (!txHistoryUnique[tx.txid]) { + ret.push(tx); + txHistoryUnique[tx.txid] = true; } else { $log.debug('Ignoring duplicate TX in history: ' + tx.txid) } }); + + return ret; }; self.updateAlias = function() { @@ -740,13 +745,6 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); }; - self.stopSync = function(remoteTx, localTx) { - if (remoteTx.txid == localTx.txid) - return true; - else - return false; - } - self.removeSoftConfirmedTx = function(txs) { return lodash.map(txs, function(tx) { if (tx.confirmations >= SOFT_CONFIRMATION_LIMIT) @@ -778,60 +776,84 @@ angular.module('copayApp.controllers').controller('indexController', function($r } self.updateLocalTxHistory = function(cb) { + var requestLimit = 6; + self.getConfirmedTxs(function(err, txsFromLocal) { if (err) return cb(err); + var endingTxid = txsFromLocal[0] ? txsFromLocal[0].txid : null; + console.log('[index.js.791:endingTxid:]', endingTxid); //TODO - var fc = profileService.focusedClient; - var c = fc.credentials; - fillTxsObject(); + function getNewTxs(newTxs, skip, i_cb) { - function fillTxsObject(txsResult, index) { - txsResult = txsResult || []; - index = index || 0; + self.getTxsFromServer(skip, endingTxid, requestLimit, function(err, res, shouldContinue) { + if (err) return i_cb(err); - self.makeTxHistoryRequest(txsResult, index, txsFromLocal[0], function(err, newIndex, exitLoop) { - if (err) return cb(err); - if (exitLoop) { - self.txHistory = []; - self.setTxHistory(lodash.compact(txsResult.concat(txsFromLocal))); - return storageService.setTxHistory(JSON.stringify(self.txHistory), c.walletId, function() { - return cb(null); - }); + + newTxs = newTxs.concat(res); + skip = skip + requestLimit; + + $log.debug('Syncing TXs. Got:' + newTxs.length + ' Skip:' + skip, ' EndingTxid:', endingTxid, ' Continue:', shouldContinue); + + if (!shouldContinue) { + newTxs = self.processNewTxs(newTxs); + $log.debug('Finish Sync: New Txs: ' + newTxs.length); + return i_cb(null, newTxs); } - fillTxsObject(txsResult, newIndex); + + self.txProgress = newTxs.length; + $timeout(function(){ + $rootScope.$apply(); + }); + getNewTxs(newTxs, skip, i_cb); }); }; + + getNewTxs([], 0, function(err, txs) { + if (err) return cb(err); + + var newHistory = lodash.compact(txs.concat(txsFromLocal)); + $log.debug('Tx History synced. Total Txs: ' + newHistory.length); + + self.completeHistory = newHistory; + self.txHistory = newHistory.slice(0, self.historyShowLimit); + self.historyShowShowAll = newHistory.length >= self.historyShowLimit; + + var fc = profileService.focusedClient; + var c = fc.credentials; + return storageService.setTxHistory(JSON.stringify(newHistory), c.walletId, function() { + return cb(); + }); + }); }); } + self.showAllHistory = function() { + self.historyShowShowAll = false; + self.txHistory = self.completeHistory; + $timeout(function() { + $rootScope.$apply(); + }); + }; + + self.getTxsFromServer = function(skip, endingTxid, limit, cb) { + var res = []; - self.makeTxHistoryRequest = function(txsResult, index, endingTx, cb) { var fc = profileService.focusedClient; - var c = fc.credentials; - var exitLoop = false; - fc.getTxHistory({ - skip: index, - limit: self.limitHistory + 1 - }, function(err, txsFromBWC) { + skip: skip, + limit: limit + }, function(err, txsFromServer) { if (err) return cb(err); - if (!txsFromBWC[0]) - exitLoop = true; + if (!txsFromServer.length) + return cb(); - lodash.each(txsFromBWC, function(t) { - if (!endingTx) txsResult.push(t); - else { - if (!self.stopSync(t, endingTx) && !exitLoop) { - txsResult.push(t); - } else { - exitLoop = true; - } - } + var res = lodash.takeWhile(txsFromServer, function(tx) { + return tx.txid != endingTxid; }); - index = index + self.limitHistory; - return cb(null, index, exitLoop); + + return cb(null, res, res.length == limit); }); - } + }; self.updateHistory = function() { var fc = profileService.focusedClient; From 0b2ddeb8d44c4ecb0ba2e1eee318a61d1e5aa6f6 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 6 Nov 2015 15:32:10 -0300 Subject: [PATCH 14/21] rm copayer modal --- public/views/modals/copayers.html | 28 ---------------------- public/views/preferencesInformation.html | 13 ++++++++++ public/views/walletHome.html | 2 +- src/js/controllers/index.js | 9 ++++--- src/js/controllers/walletHome.js | 30 ------------------------ 5 files changed, 20 insertions(+), 62 deletions(-) delete mode 100644 public/views/modals/copayers.html diff --git a/public/views/modals/copayers.html b/public/views/modals/copayers.html deleted file mode 100644 index fac3c376a..000000000 --- a/public/views/modals/copayers.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - diff --git a/public/views/preferencesInformation.html b/public/views/preferencesInformation.html index 56eedefc4..bd67a0fc2 100644 --- a/public/views/preferencesInformation.html +++ b/public/views/preferencesInformation.html @@ -15,6 +15,7 @@ +
  • Wallet Id @@ -29,6 +30,8 @@
  • + +
  • Wallet Network @@ -52,6 +55,16 @@
  • +

    Copayers

    +
  • + + {{copayer.name}} ({{'Me'|translate}}) + + + {{copayer.name}} + +
  • +

    Extended Public Keys

  • diff --git a/public/views/walletHome.html b/public/views/walletHome.html index ebe25ccb1..4030ccbd0 100644 --- a/public/views/walletHome.html +++ b/public/views/walletHome.html @@ -118,7 +118,7 @@
    -
    +

    {{(index.alias || index.walletName)}}

    diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index 61eeb9de7..9ef91e941 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -828,9 +828,9 @@ angular.module('copayApp.controllers').controller('indexController', function($r } self.showAllHistory = function() { self.historyShowShowAll = false; - self.txHistory = self.completeHistory; $timeout(function() { $rootScope.$apply(); + self.txHistory = self.completeHistory; }); }; @@ -857,7 +857,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.updateHistory = function() { var fc = profileService.focusedClient; - if (!fc.isComplete()) return; + if (!fc.isComplete() || self.updatingTxHistory) return; $log.debug('Updating Transaction History'); self.txHistoryError = false; @@ -865,9 +865,12 @@ angular.module('copayApp.controllers').controller('indexController', function($r $timeout(function() { self.updateLocalTxHistory(function(err) { - if (err) self.txHistoryError = true; self.updatingTxHistory = false; self.showWaitingSign = false; + + if (err) + self.txHistoryError = true; + $rootScope.$apply(); }); }); diff --git a/src/js/controllers/walletHome.js b/src/js/controllers/walletHome.js index 60b9e42b0..ae84e4ba4 100644 --- a/src/js/controllers/walletHome.js +++ b/src/js/controllers/walletHome.js @@ -106,36 +106,6 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi var cancel_msg = gettextCatalog.getString('Cancel'); var confirm_msg = gettextCatalog.getString('Confirm'); - $scope.openCopayersModal = function(copayers, copayerId) { - $rootScope.modalOpened = true; - var fc = profileService.focusedClient; - - var ModalInstanceCtrl = function($scope, $modalInstance) { - $scope.copayers = copayers; - $scope.copayerId = copayerId; - $scope.color = fc.backgroundColor; - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - }; - var modalInstance = $modal.open({ - templateUrl: 'views/modals/copayers.html', - windowClass: animationService.modalAnimated.slideUp, - controller: ModalInstanceCtrl, - }); - - var disableCloseModal = $rootScope.$on('closeModal', function() { - modalInstance.dismiss('cancel'); - }); - - modalInstance.result.finally(function() { - $rootScope.modalOpened = false; - disableCloseModal(); - var m = angular.element(document.getElementsByClassName('reveal-modal')); - m.addClass(animationService.modalAnimated.slideOutDown); - }); - }; - $scope.openDestinationAddressModal = function(wallets, address) { $rootScope.modalOpened = true; var fc = profileService.focusedClient; From 0be72abdfcc36aa1ecc9ad1d42393b01eb9e8f5a Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 6 Nov 2015 16:17:07 -0300 Subject: [PATCH 15/21] add spinner for historyRendering --- public/views/walletHome.html | 15 +++++++++++++-- src/js/controllers/index.js | 6 +++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/public/views/walletHome.html b/public/views/walletHome.html index 4030ccbd0..60ec07f89 100644 --- a/public/views/walletHome.html +++ b/public/views/walletHome.html @@ -457,7 +457,7 @@
    -
    +
    @@ -521,7 +521,18 @@
    - +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
  • +
  • + Hardware Wallet + + {{index.externalSource}} + +
  • + +
  • + + + No private key + +
  • + +
  • + Account ({{derivationStrategy}}) + + #{{index.account}} + +
  • + + +

    Copayers

  • diff --git a/src/js/controllers/preferencesInformation.js b/src/js/controllers/preferencesInformation.js index 3969f26b5..8f04e8077 100644 --- a/src/js/controllers/preferencesInformation.js +++ b/src/js/controllers/preferencesInformation.js @@ -7,7 +7,7 @@ angular.module('copayApp.controllers').controller('preferencesInformation', var c = fc.credentials; this.init = function() { - var basePath = profileService.getUtils().getBaseAddressDerivationPath(c.derivationStrategy, c.network, 0); + var basePath = profileService.getUtils().getBaseAddressDerivationPath(c.derivationStrategy, c.network, c.account); $scope.walletName = c.walletName; $scope.walletId = c.walletId; diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index 2def22e4b..8ac0ead58 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -46,8 +46,6 @@ angular.module('copayApp.services') client.setNotificationsInterval(BACKGROUND_UPDATE_PERIOD); }); root.focusedClient.setNotificationsInterval(FOREGROUND_UPDATE_PERIOD); - - console.log('[profileService.js.49] SETTING...'); //TODO } return cb(); From 1e9b99981e673b8b463f38218e284b8e21fb916f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 10 Nov 2015 16:43:27 -0300 Subject: [PATCH 19/21] update bwc --- bower.json | 2 +- public/views/includes/walletInfo.html | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 40753dd4d..94551fa1a 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,7 @@ ], "dependencies": { "angular": "1.4.6", - "angular-bitcore-wallet-client": "1.1.2", + "angular-bitcore-wallet-client": "1.1.5", "angular-foundation": "0.7.0", "angular-gettext": "2.1.0", "angular-moment": "0.10.1", diff --git a/public/views/includes/walletInfo.html b/public/views/includes/walletInfo.html index 920537b6f..df2c921d7 100644 --- a/public/views/includes/walletInfo.html +++ b/public/views/includes/walletInfo.html @@ -2,7 +2,10 @@ {{index.m}}-of-{{index.n}} - #{{index.account || 0}} + + + + From 232b78ee8cd626a4ba69d4a78bbdbd3562f91804 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 10 Nov 2015 20:05:05 -0300 Subject: [PATCH 20/21] fix history sync --- bower.json | 2 +- public/views/includes/walletInfo.html | 9 ++- public/views/walletHome.html | 8 +-- src/js/controllers/create.js | 1 - src/js/controllers/index.js | 70 ++++++++++++-------- src/js/controllers/preferences.js | 2 + src/js/controllers/preferencesBwsUrl.js | 4 +- src/js/controllers/preferencesInformation.js | 2 +- src/js/services/hwWallet.js | 36 +++++----- src/js/services/ledger.js | 12 ++-- src/js/services/profileService.js | 13 ++-- src/js/services/trezor.js | 8 ++- 12 files changed, 95 insertions(+), 72 deletions(-) diff --git a/bower.json b/bower.json index 94551fa1a..45f6bf216 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,7 @@ ], "dependencies": { "angular": "1.4.6", - "angular-bitcore-wallet-client": "1.1.5", + "angular-bitcore-wallet-client": "1.1.6", "angular-foundation": "0.7.0", "angular-gettext": "2.1.0", "angular-moment": "0.10.1", diff --git a/public/views/includes/walletInfo.html b/public/views/includes/walletInfo.html index df2c921d7..4b9af85c5 100644 --- a/public/views/includes/walletInfo.html +++ b/public/views/includes/walletInfo.html @@ -1,11 +1,16 @@ {{index.m}}-of-{{index.n}} - + + #{{index.account || 0}} - + + + + + diff --git a/public/views/walletHome.html b/public/views/walletHome.html index 0c48b107b..b91cde671 100644 --- a/public/views/walletHome.html +++ b/public/views/walletHome.html @@ -442,7 +442,7 @@ -->
    -
    +
    @@ -455,7 +455,7 @@
    -
    +
    @@ -465,11 +465,11 @@
    -
    +
    Initial transaction history synchronization can take some minutes for wallets with many transactions.
    Please stand by.
    -
    +
    {{index.txProgress}} Transactions
    Downloaded
    diff --git a/src/js/controllers/create.js b/src/js/controllers/create.js index 0a19e1726..490cbd82c 100644 --- a/src/js/controllers/create.js +++ b/src/js/controllers/create.js @@ -96,7 +96,6 @@ angular.module('copayApp.controllers').controller('createController', myName: $scope.totalCopayers > 1 ? form.myName.$modelValue : null, networkName: form.isTestnet.$modelValue ? 'testnet' : 'livenet', bwsurl: $scope.bwsurl, - use48: $scope.fromHardware, }; var setSeed = self.seedSourceId == 'set'; if (setSeed) { diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index ca78dc132..9184fc5db 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -8,6 +8,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.isSafari = isMobile.Safari(); self.onGoingProcess = {}; self.historyShowLimit = 10; + self.updatingTxHistory = {}; function strip(number) { return (parseFloat(number.toPrecision(12))); @@ -123,6 +124,7 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.initGlidera(); + self.setCustomBWSFlag(); if (fc.isPrivKeyExternal()) { self.needsBackup = false; self.openWallet(); @@ -135,6 +137,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); }; + self.setCustomBWSFlag = function() { + var defaults = configService.getDefaults(); + var config = configService.getSync(); + + self.usingCustomBWS = config.bwsFor && (config.bwsFor[self.walletId] != defaults.bws.url); + }; + self.setTab = function(tab, reset, tries, switchState) { tries = tries || 0; @@ -753,17 +762,14 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); } - self.getConfirmedTxs = function(cb) { - var fc = profileService.focusedClient; - var c = fc.credentials; + self.getConfirmedTxs = function(walletId, cb) { - storageService.getTxHistory(c.walletId, function(err, txs) { + storageService.getTxHistory(walletId, function(err, txs) { if (err) return cb(err); var localTxs = []; if (!txs) { - self.showWaitingSign = true; return cb(null, localTxs); } @@ -776,20 +782,21 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); } - self.updateLocalTxHistory = function(cb) { + self.updateLocalTxHistory = function(client, cb) { var requestLimit = 6; + var walletId = client.credentials.walletId; - self.getConfirmedTxs(function(err, txsFromLocal) { + self.getConfirmedTxs(walletId, function(err, txsFromLocal) { if (err) return cb(err); var endingTxid = txsFromLocal[0] ? txsFromLocal[0].txid : null; function getNewTxs(newTxs, skip, i_cb) { - self.getTxsFromServer(skip, endingTxid, requestLimit, function(err, res, shouldContinue) { + self.getTxsFromServer(client, skip, endingTxid, requestLimit, function(err, res, shouldContinue) { if (err) return i_cb(err); - newTxs = newTxs.concat(res); + newTxs = newTxs.concat(lodash.compact(res)); skip = skip + requestLimit; $log.debug('Syncing TXs. Got:' + newTxs.length + ' Skip:' + skip, ' EndingTxid:', endingTxid, ' Continue:', shouldContinue); @@ -800,8 +807,10 @@ angular.module('copayApp.controllers').controller('indexController', function($r return i_cb(null, newTxs); } - self.txProgress = newTxs.length; - $timeout(function(){ + if (walletId == profileService.focusedClient.credentials.walletId) + self.txProgress = newTxs.length; + + $timeout(function() { $rootScope.$apply(); }); getNewTxs(newTxs, skip, i_cb); @@ -814,13 +823,13 @@ angular.module('copayApp.controllers').controller('indexController', function($r var newHistory = lodash.compact(txs.concat(txsFromLocal)); $log.debug('Tx History synced. Total Txs: ' + newHistory.length); - self.completeHistory = newHistory; - self.txHistory = newHistory.slice(0, self.historyShowLimit); - self.historyShowShowAll = newHistory.length >= self.historyShowLimit; + if (walletId == profileService.focusedClient.credentials.walletId) { + self.completeHistory = newHistory; + self.txHistory = newHistory.slice(0, self.historyShowLimit); + self.historyShowShowAll = newHistory.length >= self.historyShowLimit; + } - var fc = profileService.focusedClient; - var c = fc.credentials; - return storageService.setTxHistory(JSON.stringify(newHistory), c.walletId, function() { + return storageService.setTxHistory(JSON.stringify(newHistory), walletId, function() { return cb(); }); }); @@ -838,11 +847,10 @@ angular.module('copayApp.controllers').controller('indexController', function($r }); }; - self.getTxsFromServer = function(skip, endingTxid, limit, cb) { + self.getTxsFromServer = function(client, skip, endingTxid, limit, cb) { var res = []; - var fc = profileService.focusedClient; - fc.getTxHistory({ + client.getTxHistory({ skip: skip, limit: limit }, function(err, txsFromServer) { @@ -861,18 +869,18 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.updateHistory = function() { var fc = profileService.focusedClient; - if (!fc.isComplete() || self.updatingTxHistory) return; + var walletId = fc.credentials.walletId; + + if (!fc.isComplete() || self.updatingTxHistory[walletId]) return; $log.debug('Updating Transaction History'); self.txHistoryError = false; - self.updatingTxHistory = true; + self.updatingTxHistory[walletId] = true; $timeout(function() { - self.updateLocalTxHistory(function(err) { - self.updatingTxHistory = false; - self.showWaitingSign = false; - - if (err) + self.updateLocalTxHistory(fc, function(err) { + self.updatingTxHistory[walletId] = false; + if (err) self.txHistoryError = true; $rootScope.$apply(); @@ -1325,4 +1333,12 @@ angular.module('copayApp.controllers').controller('indexController', function($r self.setFocusedWallet(); }); }); + + $rootScope.$on('Local/NewEncryptionSetting', function() { + var fc = profileService.focusedClient; + self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted(); + $timeout(function() { + $rootScope.$apply(); + }); + }); }); diff --git a/src/js/controllers/preferences.js b/src/js/controllers/preferences.js index d5c29fadf..b06098502 100644 --- a/src/js/controllers/preferences.js +++ b/src/js/controllers/preferences.js @@ -55,6 +55,7 @@ angular.module('copayApp.controllers').controller('preferencesController', return; } profileService.setPrivateKeyEncryptionFC(password, function() { + $rootScope.$emit('Local/NewEncryptionSetting'); $scope.encrypt = true; }); }); @@ -66,6 +67,7 @@ angular.module('copayApp.controllers').controller('preferencesController', return; } profileService.disablePrivateKeyEncryptionFC(function(err) { + $rootScope.$emit('Local/NewEncryptionSetting'); if (err) { $scope.encrypt = true; $log.error(err); diff --git a/src/js/controllers/preferencesBwsUrl.js b/src/js/controllers/preferencesBwsUrl.js index c27786220..d7d4afc6d 100644 --- a/src/js/controllers/preferencesBwsUrl.js +++ b/src/js/controllers/preferencesBwsUrl.js @@ -13,7 +13,7 @@ angular.module('copayApp.controllers').controller('preferencesBwsUrlController', this.bwsurl = (config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url; this.resetDefaultUrl = function() { - this.bwsurl = 'https://bws.bitpay.com/bws/api'; + this.bwsurl = defaults.bws.url; }; this.save = function() { @@ -50,4 +50,4 @@ angular.module('copayApp.controllers').controller('preferencesBwsUrlController', }); }); }; - }); \ No newline at end of file + }); diff --git a/src/js/controllers/preferencesInformation.js b/src/js/controllers/preferencesInformation.js index 8f04e8077..6ecbcfcab 100644 --- a/src/js/controllers/preferencesInformation.js +++ b/src/js/controllers/preferencesInformation.js @@ -7,7 +7,7 @@ angular.module('copayApp.controllers').controller('preferencesInformation', var c = fc.credentials; this.init = function() { - var basePath = profileService.getUtils().getBaseAddressDerivationPath(c.derivationStrategy, c.network, c.account); + var basePath = c.getBaseAddressDerivationPath(); $scope.walletName = c.walletName; $scope.walletId = c.walletId; diff --git a/src/js/services/hwWallet.js b/src/js/services/hwWallet.js index cf81f71f1..55d3ccc79 100644 --- a/src/js/services/hwWallet.js +++ b/src/js/services/hwWallet.js @@ -16,28 +16,28 @@ angular.module('copayApp.services') return msg; }; - root.getAddressPath = function(isMultisig, account) { - var rootPath; - if (account) { - rootPath = isMultisig ? root.MULTISIG_ROOTPATH : root.UNISIG_ROOTPATH; - } else { - // Old ledger wallet compat - rootPath = 44; - } - return rootPath + "'/" + root.LIVENET_PATH + "'/" + account + "'"; + root.getRootPath = function(device, isMultisig, account) { + if (!isMultisig) return root.UNISIG_ROOTPATH; + + // Compat + if (device == 'ledger' && account ==0) return root.UNISIG_ROOTPATH; + + return root.MULTISIG_ROOTPATH; + }; + + root.getAddressPath = function(device, isMultisig, account) { + return root.getRootPath(device,isMultisig,account) + "'/" + root.LIVENET_PATH + "'/" + account + "'"; } - root.getEntropyPath = function(isMultisig, account) { + root.getEntropyPath = function(device, isMultisig, account) { var path; - if (account) { - var rootPath = isMultisig ? root.MULTISIG_ROOTPATH : root.UNISIG_ROOTPATH; - path = root.ENTROPY_INDEX_PATH + rootPath + "'/" + account + "'"; - } else { - // Old ledger wallet compat - path = root.ENTROPY_INDEX_PATH + "0'"; - } - return path; + + // Old ledger wallet compat + if (device == 'ledger' && account == 0) + return root.ENTROPY_INDEX_PATH + "0'"; + + return root.ENTROPY_INDEX_PATH + root.getRootPath(device,isMultisig,account) + "'/" + account + "'"; }; root.pubKeyToEntropySource = function(xPubKey) { diff --git a/src/js/services/ledger.js b/src/js/services/ledger.js index 9da58ace1..bb54ae0ab 100644 --- a/src/js/services/ledger.js +++ b/src/js/services/ledger.js @@ -13,7 +13,7 @@ angular.module('copayApp.services') } root.getEntropySource = function(isMultisig, account, callback) { - root.getXPubKey(hwWallet.getEntropyPath(isMultisig, account), function(data) { + root.getXPubKey(hwWallet.getEntropyPath('ledger', isMultisig, account), function(data) { if (!data.success) return callback(hwWallet._err(data)); @@ -37,7 +37,7 @@ angular.module('copayApp.services') if (err) return callback(err); opts.entropySource = entropySource; - root.getXPubKey(hwWallet.getAddressPath(isMultisig, account), function(data) { + root.getXPubKey(hwWallet.getAddressPath('ledger', isMultisig, account), function(data) { if (!data.success) { $log.warn(data.message); return callback(data); @@ -45,6 +45,9 @@ angular.module('copayApp.services') opts.extendedPublicKey = data.xpubkey; opts.externalSource = 'ledger'; opts.account = account; + + // Old ledger compat + opts.derivationStrategy = account ? 'BIP48' : 'BIP44'; return callback(null, opts); }); }); @@ -54,11 +57,10 @@ angular.module('copayApp.services') root.callbacks["sign_p2sh"] = callback; var redeemScripts = []; var paths = []; - - var tx = bwcService.getUtils().buildTx(txp); + var tx = bwcService.buildTx(txp); for (var i = 0; i < tx.inputs.length; i++) { redeemScripts.push(new ByteString(tx.inputs[i].redeemScript.toBuffer().toString('hex'), GP.HEX).toString()); - paths.push(hwWallet.getAddressPath(isMultisig, account) + txp.inputs[i].path.substring(1)); + paths.push(hwWallet.getAddressPath('ledger', isMultisig, account) + txp.inputs[i].path.substring(1)); } var splitTransaction = root._splitTransaction(new ByteString(tx.toString(), GP.HEX)); var inputs = []; diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index 8ac0ead58..bb55cf604 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -173,12 +173,6 @@ angular.module('copayApp.services') var walletClient = bwcService.getClient(); var network = opts.networkName || 'livenet'; - // TODO refactor this and use bwc contants? - var derivationStrategy = 'BIP44'; - if (opts.fromHardware && opts.n > 1) { - derivationStrategy = 'BIP48'; - } - if (opts.mnemonic) { try { @@ -187,7 +181,7 @@ angular.module('copayApp.services') network: network, passphrase: opts.passphrase, account: opts.account || 0, - derivationStrategy: derivationStrategy, + derivationStrategy: opts.derivationStrategy || 'BIP44', }); } catch (ex) { @@ -205,7 +199,7 @@ angular.module('copayApp.services') try { walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, { account: opts.account || 0, - derivationStrategy: derivationStrategy, + derivationStrategy: opts.derivationStrategy || 'BIP44', }); } catch (ex) { $log.warn("Creating wallet from Extended Public Key Arg:", ex, opts); @@ -319,7 +313,7 @@ angular.module('copayApp.services') walletId: walletId }); - delete root.walletClients[walletId]; + delete root.walletClients[walletId]; root.focusedClient = null; storageService.clearLastAddress(walletId, function(err) { @@ -446,6 +440,7 @@ angular.module('copayApp.services') walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, { account: opts.account || 0, + derivationStrategy: opts.derivationStrategy || 'BIP44', }, function(err) { if (err) { diff --git a/src/js/services/trezor.js b/src/js/services/trezor.js index 4a16df8de..7c1827814 100644 --- a/src/js/services/trezor.js +++ b/src/js/services/trezor.js @@ -8,7 +8,7 @@ angular.module('copayApp.services') root.callbacks = {}; root.getEntropySource = function(isMultisig, account, callback) { - root.getXPubKey(hwWallet.getEntropyPath(isMultisig, account), function(data) { + root.getXPubKey(hwWallet.getEntropyPath('trezor', isMultisig, account), function(data) { if (!data.success) return callback(hwWallet._err(data)); @@ -31,13 +31,17 @@ angular.module('copayApp.services') $log.debug('Waiting TREZOR to settle...'); $timeout(function() { - root.getXPubKey(hwWallet.getAddressPath(isMultisig, account), function(data) { + root.getXPubKey(hwWallet.getAddressPath('trezor', isMultisig, account), function(data) { if (!data.success) return callback(hwWallet._err(data)); opts.extendedPublicKey = data.xpubkey; opts.externalSource = 'trezor'; opts.account = account; + + if (isMultisig) + opts.derivationStrategy = 'BIP48'; + return callback(null, opts); }); }, SETTLE_TIME); From be1c5e537a6153047578ead5b769fe423aedd707 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 10 Nov 2015 20:14:06 -0300 Subject: [PATCH 21/21] fix rendering timeout --- src/js/controllers/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/controllers/index.js b/src/js/controllers/index.js index 9184fc5db..26154dd1b 100644 --- a/src/js/controllers/index.js +++ b/src/js/controllers/index.js @@ -843,8 +843,8 @@ angular.module('copayApp.controllers').controller('indexController', function($r $timeout(function() { self.historyRendering = false; self.txHistory = self.completeHistory; - }); - }); + }, 100); + }, 100); }; self.getTxsFromServer = function(client, skip, endingTxid, limit, cb) {