import now working with ledger

Signed-off-by: Matias Alejo Garcia <ematiu@gmail.com>
This commit is contained in:
Matias Alejo Garcia 2015-09-04 21:18:20 -03:00
commit 4d9a477ae5
10 changed files with 148 additions and 47 deletions

View file

@ -17,7 +17,7 @@
"ng-lodash": "~0.2.0", "ng-lodash": "~0.2.0",
"angular-moment": "0.10.1", "angular-moment": "0.10.1",
"moment": "2.10.3", "moment": "2.10.3",
"angular-bitcore-wallet-client": "0.2.0", "angular-bitcore-wallet-client": "0.2.2",
"angular-ui-router": "~0.2.13", "angular-ui-router": "~0.2.13",
"qrcode-decoder-js": "*", "qrcode-decoder-js": "*",
"fastclick": "*", "fastclick": "*",

View file

@ -102,27 +102,27 @@
</div> </div>
<div ng-hide="hideAdv" class="row"> <div ng-hide="hideAdv" class="row">
<div class="large-12 columns"> <div class="large-12 columns">
<label ng-show="create.isChromeApp()" for="hw-ledger" class="line-b oh"> <label ng-show="create.isChromeApp()" for="hw-ledger" class="oh">
<span translate>Use Ledger hardware wallet</span> <span translate>Use Ledger hardware wallet</span>
<switch id="hw-ledger" name="hwLedger" ng-model="hwLedger" ng-change="isTestnet=false" class="green right m5t m10b"></switch> <switch id="hw-ledger" name="hwLedger" ng-model="hwLedger" ng-change="isTestnet=false" class="green right m5t m10b"></switch>
</label> </label>
<div ng-hide="!hwLedger"> <div ng-show="hwLedger">
<label class="line-b oh"><span translate>Select slot number for Ledger key</span> <label class="oh line-b "><span translate>Select slot number for Ledger key</span>
<select class="m10t" ng-model="externalIndex" ng-options="externalIndex as externalIndex for externalIndex in create.externalIndexValues"> <select class="m10t" ng-model="externalIndex" ng-options="externalIndex as externalIndex for externalIndex in create.externalIndexValues">
</select> </select>
</label> </label>
</div> </div>
<label for="network-name" class="oh"> <label for="network-name" class="oh" ng-show="!hwLedger">
<span translate>Testnet</span> <span translate>Testnet</span>
<switch id="network-name" name="isTestnet" ng-model="isTestnet" class="green right m5t m10b"></switch> <switch id="network-name" name="isTestnet" ng-model="isTestnet" class="green right m5t m10b"></switch>
</label> </label>
<label ng-hide="hwLedger" for="seed" class="oh"> <label ng-show="!hwLedger" for="seed" class="oh">
<span translate>Specify your wallet seed</span> <span translate>Specify your wallet seed</span>
<switch id="seed" name="setSeed" ng-model="setSeed" class="green right m5t m10b"></switch> <switch id="seed" name="setSeed" ng-model="setSeed" class="green right m5t m10b"></switch>
</label> </label>
<label for="createPassphrase" class="line-b oh" ng-show="!setSeed" ><span translate>Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small> <label for="createPassphrase" class="line-b oh" ng-show="!setSeed && !hwLedger" ><span translate>Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small>
<div class="input"> <div class="input">
<input type="text" class="form-control" <input type="text" class="form-control"
name="createPassphrase" ng-model="createPassphrase"> name="createPassphrase" ng-model="createPassphrase">
@ -136,11 +136,11 @@
type="text" type="text"
name="privateKey" ng-model="privateKey"> name="privateKey" ng-model="privateKey">
</label> </label>
<label for="passphrase" class="line-b oh" ng-show="setSeed && !hwLedger"><span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small> <label for="passphrase" class="line-b oh" ng-show="setSeed && !hwLedger"><span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small>
<div class="input"> <div class="input">
<input type="text" class="form-control" name="passphrase" ng-model="passphrase"> <input type="text" class="form-control" name="passphrase" ng-model="passphrase">
</div> </div>
</label> </label>
</div> </div>
</div> </div>

View file

@ -5,7 +5,7 @@
</div> </div>
<div class="content p20v" ng-controller="importController as import" ng-init="type='12'"> <div class="content p20v" ng-controller="importController as import" ng-init="type='12'">
<div class="onGoingProcess" ng-show="import.loading"> <div class="onGoingProcess" ng-show="import.loading && !import.ledger">
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}"> <div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
<div class="spinner"> <div class="spinner">
<div class="rect1"></div> <div class="rect1"></div>
@ -17,9 +17,23 @@
<span translate>Importing wallet...</span> <span translate>Importing wallet...</span>
</div> </div>
</div> </div>
<div class="onGoingProcess" ng-show="import.ledger">
<div class="onGoingProcess-content" ng-style="{'background-color':'#222'}">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
<span translate>Connecting to Ledger Wallet...</span>
</div>
</div>
<div class="create-tab small-only-text-center" ng-hide="create.hideTabs"> <div class="create-tab small-only-text-center" ng-hide="create.hideTabs">
<div class="row"> <div class="row" ng-show="!index.isChromeApp">
<div class="tab-container small-6 medium-6 large-6"> <div class="tab-container small-6 medium-6 large-6">
<a href <a href
ng-class="{'selected': type =='12'}" ng-class="{'selected': type =='12'}"
@ -30,14 +44,27 @@
ng-class="{'selected': type=='file'}" ng-class="{'selected': type=='file'}"
ng-click="import.setType('file')" translate>File/Text Backup</a> ng-click="import.setType('file')" translate>File/Text Backup</a>
</div> </div>
<!-- <div class="tab&#45;container small&#45;4 medium&#45;4 large&#45;4"> --> </div>
<!-- <a href --> <div class="row" ng-show="index.isChromeApp">
<!-- ng&#45;class="{'selected': type=='qr'}" --> <div class="tab-container small-4 medium-4 large-4">
<!-- ng&#45;click="import.setType('qr')" translate>QR Code</a> --> <a href
<!-- </div> --> ng-class="{'selected': type =='12'}"
ng-click="import.setType('12')" translate>Walled Seed</a>
</div>
<div class="tab-container small-4 medium-4 large-4">
<a href
ng-class="{'selected': type=='file'}"
ng-click="import.setType('file')" translate>File/Text Backup</a>
</div>
<div class="tab-container small-4 medium-4 large-4">
<a href
ng-class="{'selected': type=='ledger'}"
ng-click="import.setType('ledger')" translate>Ledger</a>
</div>
</div> </div>
</div> </div>
@ -139,9 +166,29 @@
</div> </div>
</div> </div>
<div class="row" ng-show="type == 'qr'"> <div class="row" ng-show="type == 'ledger'">
<div class="large-12 columns"> <div class="large-12 columns">
TODO <form name="importForm3" ng-submit="import.importLedger(importForm3)" novalidate>
<div class="box-notification" ng-show="import.error">
<span class="text-warning size-14">
{{import.error|translate}}
</span>
</div>
<div class="large-12 columns">
<label class="line-b oh">
<span translate>Select slot number for Ledger key</span>
<select class="m10t" ng-model="externalIndex" ng-options="externalIndex as externalIndex for externalIndex in import.externalIndexValues">
</select>
</label>
<button translate type="submit" class="button round expand black"
ng-disabled="import.loading || import.ledger">
Import backup
</button>
</div>
</form>
</div> </div>
</div> </div>

View file

@ -86,7 +86,7 @@
</a> </a>
<div ng-show="join.hideAdv" class="row"> <div ng-show="join.hideAdv" class="row">
<div class="large-12 columns" ng-show="join.isChromeApp()"> <div class="large-12 columns" ng-show="join.isChromeApp()">
<label for="hw-ledger" class="line-b oh"> <label for="hw-ledger" class="oh">
<span translate>Use Ledger hardware wallet</span> <span translate>Use Ledger hardware wallet</span>
<switch id="hw-ledger" name="hwLedger" ng-model="hwLedger" class="green right m5t m10b"></switch> <switch id="hw-ledger" name="hwLedger" ng-model="hwLedger" class="green right m5t m10b"></switch>
</label> </label>
@ -100,26 +100,26 @@
</div> </div>
<div class="large-12 columns"> <div class="large-12 columns">
<label for="seed" class="oh"> <label ng-show="!hwLedger" for="seed" class="oh">
<span translate>Specify your wallet seed</span> <span translate>Specify your wallet seed</span>
<switch id="seed" name="setSeed" ng-model="setSeed" class="green right m5t m10b"></switch> <switch id="seed" name="setSeed" ng-model="setSeed" class="green right m5t m10b"></switch>
</label> </label>
<label for="createPassphrase" class="line-b oh" ng-show="!setSeed" ><span translate>Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small> <label for="createPassphrase" class="line-b oh" ng-show="!setSeed && !hwLedger" ><span translate>Seed Passphrase</span> <small translate>Add an optional passphrase to secure the seed</small>
<div class="input"> <div class="input">
<input type="text" class="form-control" <input type="text" class="form-control"
name="createPassphrase" ng-model="createPassphrase"> name="createPassphrase" ng-model="createPassphrase">
</div> </div>
</label> </label>
<label for="ext-master" class="m10t" ng-show="setSeed"> <label for="ext-master" class="m10t" ng-show="setSeed && !hwLedger">
<span translate>Wallet Seed</span> <span translate>Wallet Seed</span>
<small translate>Enter the 12 words seed (BIP39)</small> <small translate>Enter the 12 words seed (BIP39)</small>
<input id="ext-master" <input id="ext-master"
type="text" type="text"
name="privateKey" ng-model="privateKey"> name="privateKey" ng-model="privateKey">
</label> </label>
<label for="passphrase" class="line-b oh" ng-show="setSeed"><span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small> <label for="passphrase" class="line-b oh" ng-show="setSeed && !hwLedger"><span translate>Seed Passphrase</span> <small translate>The seed could require a passphrase to be imported</small>
<div class="input"> <div class="input">
<input type="text" class="form-control" name="passphrase" ng-model="passphrase"> <input type="text" class="form-control" name="passphrase" ng-model="passphrase">
</div> </div>
@ -136,10 +136,6 @@
</span> </span>
</div> </div>
<button translate type="submit" class="button expand black m0 round" <button translate type="submit" class="button expand black m0 round"
ng-disabled="joinForm.$invalid || join.loading">Join</button> ng-disabled="joinForm.$invalid || join.loading">Join</button>
</form> </form>

View file

@ -106,7 +106,7 @@ angular.module('copayApp.controllers').controller('createController',
}); });
} }
else { else {
if ( ( opts.mnemonic && opts.n==1) || otps.externalSource ) { if ( ( opts.mnemonic && opts.n==1) || opts.externalSource ) {
$rootScope.$emit('Local/WalletImported', walletId); $rootScope.$emit('Local/WalletImported', walletId);
} else { } else {
go.walletHome(); go.walletHome();

View file

@ -1,12 +1,14 @@
'use strict'; 'use strict';
angular.module('copayApp.controllers').controller('importController', angular.module('copayApp.controllers').controller('importController',
function($scope, $rootScope, $location, $timeout, $log, profileService, notification, go, isMobile, isCordova, sjcl, gettext, lodash) { function($scope, $rootScope, $location, $timeout, $log, profileService, notification, go, isMobile, isCordova, sjcl, gettext, lodash, ledger) {
var self = this; var self = this;
this.isSafari = isMobile.Safari(); this.isSafari = isMobile.Safari();
this.isCordova = isCordova; this.isCordova = isCordova;
this.externalIndexValues = lodash.range(0, ledger.MAX_SLOT);
$scope.externalIndex = 0;
var reader = new FileReader(); var reader = new FileReader();
window.ignoreMobilePause = true; window.ignoreMobilePause = true;
@ -80,13 +82,6 @@ angular.module('copayApp.controllers').controller('importController',
}, 100); }, 100);
}; };
// {
// network: opts.network,
// m: opts.m,
// n: opts.n,
// publicKeyRing: opts.publicKeyRing,
// },
//
$scope.getFile = function() { $scope.getFile = function() {
// If we use onloadend, we need to check the readyState. // If we use onloadend, we need to check the readyState.
reader.onloadend = function(evt) { reader.onloadend = function(evt) {
@ -146,10 +141,12 @@ angular.module('copayApp.controllers').controller('importController',
if (!words) { if (!words) {
this.error = gettext('Please enter the backup words'); this.error = gettext('Please enter the backup words');
} else { } else {
var wordList = words.split(/ /).filter(function(v){ return v.length>0; }); var wordList = words.split(/ /).filter(function(v) {
return v.length > 0;
});
if (wordList.length != 12) if (wordList.length != 12)
this.error = gettext('Please enter 12 backup words'); this.error = gettext('Please enter 12 backup words');
else else
words = wordList.join(' '); words = wordList.join(' ');
} }
@ -166,4 +163,41 @@ angular.module('copayApp.controllers').controller('importController',
_importMnemonic(words, opts); _importMnemonic(words, opts);
}; };
this.importLedger = function(form) {
var self = this;
if (form.$invalid) {
this.error = gettext('There is an error in the form');
$timeout(function() {
$scope.$apply();
});
return;
}
self.ledger = true;
ledger.getInfoForNewWallet($scope.externalIndex, function(err, lopts) {
self.ledger = false;
if (err) {
self.error = err;
$scope.$apply();
return;
}
lopts.externalIndex = $scope.externalIndex;
lopts.externalSource = 'ledger';
self.loading = true;
$log.debug('Import opts', lopts);
profileService.importExtendedPublicKey(lopts, function(err, walletId) {
self.loading = false;
if (err) {
self.error = err;
return $timeout(function() {
$scope.$apply();
});
}
$rootScope.$emit('Local/WalletImported', walletId);
notification.success(gettext('Success'), gettext('Your wallet has been imported correctly'));
go.walletHome();
});
}, 100);
};
}); });

View file

@ -3,6 +3,7 @@
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) { 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) {
var self = this; var self = this;
self.isCordova = isCordova; self.isCordova = isCordova;
self.isChromeApp = isChromeApp;
self.onGoingProcess = {}; self.onGoingProcess = {};
self.limitHistory = 5; self.limitHistory = 5;
@ -653,16 +654,18 @@ angular.module('copayApp.controllers').controller('indexController', function($r
return str; return str;
} }
var step = 6;
function getHistory(skip, cb) { function getHistory(skip, cb) {
skip = skip || 0; skip = skip || 0;
fc.getTxHistory({ fc.getTxHistory({
skip: skip, skip: skip,
limit: 100 limit: step,
}, function(err, txs) { }, function(err, txs) {
if (err) return cb(err); if (err) return cb(err);
if (txs && txs.length > 0) { if (txs && txs.length > 0) {
allTxs.push(txs); allTxs.push(txs);
return getHistory(skip + 100, cb); return getHistory(skip + step, cb);
} else { } else {
return cb(null, lodash.flatten(allTxs)); return cb(null, lodash.flatten(allTxs));
} }

View file

@ -4,7 +4,7 @@ angular.module('copayApp.controllers').controller('joinController',
function($scope, $rootScope, $timeout, go, isMobile, notification, profileService, isCordova, isChromeApp, $modal, gettext, lodash, ledger) { function($scope, $rootScope, $timeout, go, isMobile, notification, profileService, isCordova, isChromeApp, $modal, gettext, lodash, ledger) {
var self = this; var self = this;
this.externalIndexValues = lodash.range(0,20); this.externalIndexValues = lodash.range(0,ledger.MAX_SLOT);
$scope.externalIndex = 0; $scope.externalIndex = 0;
this.isChromeApp = function() { this.isChromeApp = function() {

View file

@ -25,7 +25,7 @@ angular.module('copayApp.services')
body = gettextCatalog.getString('Copayer already in this wallet'); body = gettextCatalog.getString('Copayer already in this wallet');
break; break;
case 'COPAYER_REGISTERED': case 'COPAYER_REGISTERED':
body = gettextCatalog.getString('Copayer already registered'); body = gettextCatalog.getString('Wallet already registered');
break; break;
case 'COPAYER_VOTED': case 'COPAYER_VOTED':
body = gettextCatalog.getString('Copayer already voted on this spend proposal'); body = gettextCatalog.getString('Copayer already voted on this spend proposal');

View file

@ -179,8 +179,9 @@ angular.module('copayApp.services')
} }
} else if (opts.extendedPublicKey) { } else if (opts.extendedPublicKey) {
try { try {
walletClient.seedFromExternalWalletPublicKey(opts.extendedPublicKey, opts.externalSource, opts.externalIndex, opts.entropySource); walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.externalIndex, opts.entropySource);
} catch (ex) { } catch (ex) {
$log.warn(ex);
return cb(gettext('Could not create using the specified extended public key')); return cb(gettext('Could not create using the specified extended public key'));
} }
} else { } else {
@ -191,7 +192,7 @@ angular.module('copayApp.services')
$log.info('Error creating seed: ' + e.message); $log.info('Error creating seed: ' + e.message);
if (e.message.indexOf('language') > 0) { if (e.message.indexOf('language') > 0) {
$log.info('Using default language for mnemonic'); $log.info('Using default language for mnemonic');
walletClient.seedFromRandomWithMnemonic(network, opts.passphrase); walletClient.seedFromRandomWithMnemonic(network, opts.passphrase);
} else { } else {
return cb(e); return cb(e);
} }
@ -358,6 +359,26 @@ angular.module('copayApp.services')
}); });
}; };
root.importExtendedPublicKey = function(opts, cb) {
var walletClient = bwcService.getClient();
$log.debug('Importing Wallet XPubKey');
walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.externalIndex, opts.entropySource, function(err) {
if (err) {
// in HW wallets, req key is always the same. They can't addAccess.
if (err.code == 'NOT_AUTHORIZED')
err.code = 'WALLET_DOES_NOT_EXIST';
return bwsError.cb(err, gettext('Could not import'), cb);
}
root._addWalletClient(walletClient, cb);
});
};
root.create = function(opts, cb) { root.create = function(opts, cb) {
$log.info('Creating profile'); $log.info('Creating profile');
configService.get(function(err) { configService.get(function(err) {