Merge pull request #4 from bitpay/feat/encrypted-wallet

Feat/encrypted wallet
This commit is contained in:
Gustavo Maximiliano Cortez 2016-08-29 17:35:54 -03:00 committed by GitHub
commit de9ae56557
20 changed files with 253 additions and 446 deletions

View file

@ -42,7 +42,7 @@
"url": "https://github.com/bitpay/copay/issues" "url": "https://github.com/bitpay/copay/issues"
}, },
"dependencies": { "dependencies": {
"bitcore-wallet-client": "2.11.0", "bitcore-wallet-client": "4.0.0",
"coveralls": "^2.11.9", "coveralls": "^2.11.9",
"express": "^4.11.2", "express": "^4.11.2",
"fs": "0.0.2", "fs": "0.0.2",

View file

@ -5,7 +5,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "BitPay", "name": "BitPay",
"description": "The BitPay Bitcoin Wallet", "description": "The BitPay Bitcoin Wallet",
"version": "0.3.0", "version": "0.6.0",
"permissions": [ "permissions": [
"storage", "storage",
"unlimitedStorage", "unlimitedStorage",

View file

@ -1,12 +1,12 @@
{ {
"//":"PLEASE! Do not edit this file directly", "//":"PLEASE! Do not edit this file directly",
"//":" Modify it at app-template/", "//":" Modify it at app-template/",
"name": "bitpay", "name": "bitpay",
"description": "The BitPay Bitcoin Wallet", "description": "The BitPay Bitcoin Wallet",
"author": "BitPay", "author": "BitPay",
"version": "0.3.0", "version": "0.6.0",
"keywords": [ "keywords": [
"wallet", "wallet",
"copay", "copay",
@ -23,10 +23,8 @@
"visible": true, "visible": true,
"resizable": true, "resizable": true,
"frame": true, "frame": true,
"width": 400, "width": 800,
"height": 600, "height": 600,
"min_width": 400,
"min_height": 600,
"position": "center", "position": "center",
"fullscreen": false "fullscreen": false
}, },
@ -47,7 +45,7 @@
"url": "https://github.com/bitpay/copay/issues" "url": "https://github.com/bitpay/copay/issues"
}, },
"dependencies": { "dependencies": {
"bitcore-wallet-client": "2.11.0", "bitcore-wallet-client": "4.0.0",
"coveralls": "^2.11.9", "coveralls": "^2.11.9",
"express": "^4.11.2", "express": "^4.11.2",
"fs": "0.0.2", "fs": "0.0.2",

View file

@ -7,6 +7,10 @@
  </ion-nav-buttons>   </ion-nav-buttons>
</ion-nav-bar> </ion-nav-bar>
<ion-content> <ion-content>
<div ng-show="deleted">
delete mnemonics. You can still export it.
</div >
<!-- <!--
## STEP 1 ## STEP 1
--> -->

View file

@ -20,18 +20,12 @@
<i class="icon ion-ios-arrow-right"></i> <i class="icon ion-ios-arrow-right"></i>
</div> </div>
<div class="item"> <div class="item">
<span translate>Devices</span> <span translate>Wallet Type</span>
<span class="item-note"> <span class="item-note">
1 {{wallet.m}}-of-{{wallet.n}}
</span> </span>
</div> </div>
<div class="item"> <div class="item" ng-show="wallet.isPrivKeyExternal()">
<span translate>Required number of signatures</span>
<span class="item-note">
1
</span>
</div>
<div class="item" ng-show="index.isPrivKeyExternal">
<span translate>Hardware wallet</span> <span translate>Hardware wallet</span>
<span class="item-note"> <span class="item-note">
{{wallet.externalSource}} {{wallet.externalSource}}
@ -56,15 +50,20 @@
<div class="item item-divider"> <div class="item item-divider">
Security Security
</div> </div>
<div class="item item-icon-right" href ui-sref="wallet.backup" ng-hide="index.isPrivKeyExternal"> <div class="item item-icon-right" href ui-sref="wallet.backup" ng-hide="wallet.isPrivKeyExternal()">
<span translate>Backup</span> <span translate>Backup</span>
<i class="icon ion-ios-arrow-right"></i> <i class="icon ion-ios-arrow-right"></i>
</div> </div>
<div ng-show="!index.noFocusedWallet && index.canSign"> <div ng-show="wallet.canSign()">
<ion-toggle ng-model="encryptEnabled" toggle-class="toggle-balanced" ng-change="encryptChange()"> <ion-toggle ng-model="encryptEnabled" toggle-class="toggle-balanced" ng-change="encryptChange()">
<span class="toggle-label" translate>Request Spending Password</span> <span class="toggle-label" translate>Request Spending Password</span>
</ion-toggle> </ion-toggle>
</div> </div>
<div ng-show="wallet.canSign() && touchIdAvailable">
<ion-toggle ng-model="touchIdEnabled" toggle-class="toggle-balanced" ng-change="touchIdChange()">
<span class="toggle-label" translate>Request Fingerprint</span>
</ion-toggle>
</div>
<div class="item item-icon-right" href ui-sref="wallet.deleteWords" ng-show ="!deleted"> <div class="item item-icon-right" href ui-sref="wallet.deleteWords" ng-show ="!deleted">
<span translate>Delete recovery phrase</span> <span translate>Delete recovery phrase</span>
<i class="icon ion-ios-arrow-right"></i> <i class="icon ion-ios-arrow-right"></i>

View file

@ -22,7 +22,7 @@
<a class="item text-center" ui-sref="activity" ng-show="notificationsMore"> <a class="item text-center" ui-sref="activity" ng-show="notificationsMore">
<span translate>More</span> ({{notificationsMore}}) <span translate>More</span> ({{notificationsMore}})
(ToDo: 1-1 no here yet) <span style="font-size:12px;color:gray">(ToDo: Cache, refresh & seft not. 1-1 no here yet)</span>
</a> </a>
<div class="item" ng-show="!notifications[0]"> <div class="item" ng-show="!notifications[0]">

View file

@ -6,45 +6,32 @@ angular.module('copayApp.controllers').controller('backupController',
var wallet = profileService.getWallet($stateParams.walletId); var wallet = profileService.getWallet($stateParams.walletId);
$scope.walletName = wallet.credentials.walletName; $scope.walletName = wallet.credentials.walletName;
$scope.n = wallet.n; $scope.n = wallet.n;
var keys;
$scope.credentialsEncrypted = wallet.isPrivKeyEncrypted; $scope.credentialsEncrypted = wallet.isPrivKeyEncrypted();
var isDeletedSeed = function() { var isDeletedSeed = function() {
if (lodash.isEmpty(wallet.credentials.mnemonic) && lodash.isEmpty(wallet.credentials.mnemonicEncrypted)) if (!wallet.credentials.mnemonic && !wallet.credentials.mnemonicEncrypted)
return true; return true;
return false; return false;
}; };
var handleEncryptedWallet = function(client, cb) {
if (!walletService.isEncrypted(client)) {
return cb();
}
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
if (err) return cb(err);
return cb(walletService.unlock(client, password));
});
};
$scope.init = function() { $scope.init = function() {
$scope.deleted = isDeletedSeed(); $scope.deleted = isDeletedSeed();
if ($scope.deleted) return; if ($scope.deleted) {
$log.debug('no mnemonics');
return;
}
fingerprintService.check(wallet, function(err) { walletService.getKeys(wallet, function(err, k) {
if (err) { if (err || !k) {
$state.go('preferences'); $state.go('wallet.preferences');
return; return;
} }
$scope.credentialsEncrypted = false;
handleEncryptedWallet(wallet, function(err) { keys = k;
if (err) { $scope.initFlow();
$log.warn('Error decrypting credentials:', $scope.error);
$state.go('preferences');
return;
}
$scope.credentialsEncrypted = false;
$scope.initFlow();
});
}); });
}; };
@ -60,8 +47,10 @@ angular.module('copayApp.controllers').controller('backupController',
}; };
$scope.initFlow = function() { $scope.initFlow = function() {
var words = wallet.getMnemonic(); if (!keys) return;
$scope.xPrivKey = wallet.credentials.xPrivKey;
var words = keys.mnemonic;
$scope.mnemonicWords = words.split(/[\u3000\s]+/); $scope.mnemonicWords = words.split(/[\u3000\s]+/);
$scope.shuffledMnemonicWords = shuffledWords($scope.mnemonicWords); $scope.shuffledMnemonicWords = shuffledWords($scope.mnemonicWords);
$scope.mnemonicHasPassphrase = wallet.mnemonicHasPassphrase(); $scope.mnemonicHasPassphrase = wallet.mnemonicHasPassphrase();
@ -72,6 +61,7 @@ angular.module('copayApp.controllers').controller('backupController',
$scope.selectComplete = false; $scope.selectComplete = false;
$scope.backupError = false; $scope.backupError = false;
words = lodash.repeat('x', 300);
$timeout(function() { $timeout(function() {
$scope.$apply(); $scope.$apply();
}, 10); }, 10);
@ -139,10 +129,12 @@ angular.module('copayApp.controllers').controller('backupController',
account: wallet.credentials.account account: wallet.credentials.account
}); });
} catch (err) { } catch (err) {
walletClient.credentials.xPrivKey = lodash.repeat('x', 64);
return cb(err); return cb(err);
} }
if (walletClient.credentials.xPrivKey != $scope.xPrivKey) { if (walletClient.credentials.xPrivKey.substr(walletClient.credentials.xPrivKey) != keys.xPrivKey) {
delete walletClient.credentials;
return cb('Private key mismatch'); return cb('Private key mismatch');
} }
} }
@ -205,9 +197,4 @@ angular.module('copayApp.controllers').controller('backupController',
$scope.selectComplete = false; $scope.selectComplete = false;
}; };
$scope.$on('$destroy', function() {
walletService.lock(wallet);
});
}); });

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $rootScope, $timeout, $log, $ionicModal, lodash, bitpayCardService, configService, profileService, walletService, fingerprintService, ongoingProcess, bwcError, bitcore, pbkdf2Service, moment, platformInfo) { angular.module('copayApp.controllers').controller('bitpayCardController', function($scope, $rootScope, $timeout, $log, $ionicModal, lodash, bitpayCardService, configService, profileService, walletService, ongoingProcess, pbkdf2Service, moment, platformInfo) {
var self = this; var self = this;
var wallet; var wallet;
@ -205,7 +205,6 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
}; };
walletService.createTx(wallet, txp, function(err, createdTxp) { walletService.createTx(wallet, txp, function(err, createdTxp) {
ongoingProcess.set('Processing Transaction...', false);
if (err) { if (err) {
self.error = bwcError.msg(err); self.error = bwcError.msg(err);
$timeout(function() { $timeout(function() {
@ -213,10 +212,9 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
}); });
return; return;
} }
self.confirmTx(createdTxp, function(err, tx) { walletService.publishAndSign(createdTxp, function(err, tx) {
ongoingProcess.set('Processing Transaction...', false);
if (err) { if (err) {
self.error = bwcError.msg(err); self.error = err;
$timeout(function() { $timeout(function() {
$scope.$digest(); $scope.$digest();
}); });
@ -234,55 +232,6 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
}, 100); }, 100);
}; };
this.confirmTx = function(txp, cb) {
fingerprintService.check(wallet, function(err) {
if (err) {
$log.debug(err);
return cb(err);
}
walletService.handleEncryptedWallet(wallet, function(err) {
if (err) {
$log.debug(err);
return bwcError.cb(err, null, cb);
}
ongoingProcess.set('Processing Transaction...', true);
walletService.publishTx(wallet, txp, function(err, publishedTxp) {
if (err) {
$log.debug(err);
return bwcError.cb(err, null, cb);
}
walletService.signTx(wallet, publishedTxp, function(err, signedTxp) {
walletService.lock(wallet);
if (err) {
$log.debug(err);
walletService.removeTx(wallet, signedTxp, function(err) {
if (err) $log.debug(err);
});
return bwcError.cb(err, null, cb);
}
walletService.broadcastTx(wallet, signedTxp, function(err, broadcastedTxp) {
if (err) {
$log.debug(err);
walletService.removeTx(wallet, broadcastedTxp, function(err) {
if (err) $log.debug(err);
});
return bwcError.cb(err, null, cb);
}
$timeout(function() {
return cb(null, broadcastedTxp);
}, 5000);
});
});
});
});
});
};
this.authenticate = function() { this.authenticate = function() {
self.error = null; self.error = null;

View file

@ -244,7 +244,6 @@ angular.module('copayApp.controllers').controller('buyAmazonController',
} }
walletService.signTx(wallet, publishedTxp, function(err, signedTxp) { walletService.signTx(wallet, publishedTxp, function(err, signedTxp) {
walletService.lock(wallet);
if (err) { if (err) {
$log.debug(err); $log.debug(err);
walletService.removeTx(wallet, signedTxp, function(err) { walletService.removeTx(wallet, signedTxp, function(err) {

View file

@ -189,7 +189,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
}; };
var setSendError = function(msg) { var setSendError = function(msg) {
showAlert(gettext('Error creating transaction'), msg); showAlert(gettext('Error at confirm:'), msg);
}; };
function apply(txp) { function apply(txp) {

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
angular.module('copayApp.controllers').controller('exportController', angular.module('copayApp.controllers').controller('exportController',
function($rootScope, $scope, $timeout, $log, lodash, backupService, walletService, storageService, profileService, platformInfo, notification, gettext, gettextCatalog, $state, $stateParams) { function($rootScope, $scope, $timeout, $log, lodash, backupService, walletService, storageService, profileService, platformInfo, gettext, gettextCatalog, $state, $stateParams) {
var prevState; var prevState;
var isWP = platformInfo.isWP; var isWP = platformInfo.isWP;
var isAndroid = platformInfo.isAndroid; var isAndroid = platformInfo.isAndroid;
@ -21,7 +21,7 @@ angular.module('copayApp.controllers').controller('exportController',
$scope.canSign = wallet.canSign(); $scope.canSign = wallet.canSign();
walletService.getEncodedWalletInfo(wallet, function(err, code) { walletService.getEncodedWalletInfo(wallet, function(err, code) {
if (err) { if (err || !code) {
$log.warn(err); $log.warn(err);
return $state.go('wallet.preferencesAdvanced') return $state.go('wallet.preferencesAdvanced')
} }
@ -48,10 +48,6 @@ angular.module('copayApp.controllers').controller('exportController',
}; };
*/ */
$scope.$on('$destroy', function() {
walletService.lock(wallet);
});
$scope.downloadWalletBackup = function() { $scope.downloadWalletBackup = function() {
$scope.getAddressbook(function(err, localAddressBook) { $scope.getAddressbook(function(err, localAddressBook) {
if (err) { if (err) {
@ -68,7 +64,6 @@ angular.module('copayApp.controllers').controller('exportController',
$scope.error = true; $scope.error = true;
return; return;
} }
notification.success(gettext('Success'), gettext('Encrypted export file saved'));
$state.go('tabs.home'); $state.go('tabs.home');
}); });
}); });

View file

@ -48,10 +48,6 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
}, 10); }, 10);
}; };
$scope.$on('$destroy', function() {
walletService.lock($scope.wallet);
});
$scope.reject = function(txp) { $scope.reject = function(txp) {
$scope.loading = true; $scope.loading = true;
$scope.error = null; $scope.error = null;

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
angular.module('copayApp.controllers').controller('preferencesController', angular.module('copayApp.controllers').controller('preferencesController',
function($scope, $rootScope, $timeout, $log, $stateParams, configService, profileService, fingerprintService, walletService) { function($scope, $rootScope, $timeout, $log, $stateParams, configService, profileService, fingerprintService, walletService, $state) {
var wallet = profileService.getWallet($stateParams.walletId); var wallet = profileService.getWallet($stateParams.walletId);
var walletId = wallet.credentials.walletId; var walletId = wallet.credentials.walletId;
@ -10,22 +10,20 @@ angular.module('copayApp.controllers').controller('preferencesController',
$scope.init = function() { $scope.init = function() {
$scope.externalSource = null; $scope.externalSource = null;
if (wallet) { if (!wallet)
var config = configService.getSync(); return $state.go('tabs.home');
config.aliasFor = config.aliasFor || {};
$scope.alias = config.aliasFor[walletId] || wallet.credentials.walletName;
$scope.color = config.colorFor[walletId] || '#4A90E2';
$scope.encryptEnabled = walletService.isEncrypted(wallet); var config = configService.getSync();
if (wallet.isPrivKeyExternal) config.aliasFor = config.aliasFor || {};
$scope.externalSource = wallet.getPrivKeyExternalSourceName() == 'ledger' ? 'Ledger' : 'Trezor'; $scope.alias = config.aliasFor[walletId] || wallet.credentials.walletName;
$scope.color = config.colorFor[walletId] || '#4A90E2';
// TODO externalAccount $scope.encryptEnabled = walletService.isEncrypted(wallet);
//this.externalIndex = wallet.getExternalIndex(); if (wallet.isPrivKeyExternal)
} $scope.externalSource = wallet.getPrivKeyExternalSourceName() == 'ledger' ? 'Ledger' : 'Trezor';
$scope.touchidAvailable = fingerprintService.isAvailable(); $scope.touchIdAvailable = fingerprintService.isAvailable();
$scope.touchidEnabled = config.touchIdFor ? config.touchIdFor[walletId] : null; $scope.touchIdEnabled = config.touchIdFor ? config.touchIdFor[walletId] : null;
$scope.deleted = false; $scope.deleted = false;
if (wallet.credentials && !wallet.credentials.mnemonicEncrypted && !wallet.credentials.mnemonic) { if (wallet.credentials && !wallet.credentials.mnemonicEncrypted && !wallet.credentials.mnemonic) {
@ -33,97 +31,51 @@ angular.module('copayApp.controllers').controller('preferencesController',
} }
}; };
var handleEncryptedWallet = function(cb) {
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
if (err) return cb(err);
return cb(walletService.unlock(wallet, password));
});
};
$scope.encryptChange = function() { $scope.encryptChange = function() {
if (!wallet) return; if (!wallet) return;
var val = $scope.encryptEnabled; var val = $scope.encryptEnabled;
var setPrivateKeyEncryption = function(password, cb) {
$log.debug('Encrypting private key for', wallet.credentials.walletName);
wallet.setPrivateKeyEncryption(password);
wallet.lock();
profileService.updateCredentials(JSON.parse(wallet.export()), function() {
$log.debug('Wallet encrypted');
return cb();
});
};
var disablePrivateKeyEncryption = function(cb) {
$log.debug('Disabling private key encryption for', wallet.credentials.walletName);
try {
wallet.disablePrivateKeyEncryption();
} catch (e) {
return cb(e);
}
profileService.updateCredentials(JSON.parse(wallet.export()), function() {
$log.debug('Wallet encryption disabled');
return cb();
});
};
if (val && !walletService.isEncrypted(wallet)) { if (val && !walletService.isEncrypted(wallet)) {
$rootScope.$emit('Local/NeedsPassword', true, function(err, password) { $log.debug('Encrypting private key for', wallet.name);
if (err || !password) { walletService.encrypt(wallet, function(err) {
if (err) {
$log.warn(err);
// ToDo show error?
$scope.encryptEnabled = false; $scope.encryptEnabled = false;
return; return;
} }
setPrivateKeyEncryption(password, function() { profileService.updateCredentials(JSON.parse(wallet.export()), function() {
$rootScope.$emit('Local/NewEncryptionSetting'); $log.debug('Wallet encrypted');
return;
});
})
} else if (!val && walletService.isEncrypted(wallet)) {
walletService.decrypt(wallet, function(err) {
if (err) {
$log.warn(err);
// ToDo show error?
$scope.encryptEnabled = true; $scope.encryptEnabled = true;
return;
}
profileService.updateCredentials(JSON.parse(wallet.export()), function() {
$log.debug('Wallet decrypted');
return;
}); });
}); })
} else {
if (!val && walletService.isEncrypted(wallet)) {
handleEncryptedWallet(function(err) {
if (err) {
$scope.encryptEnabled = true;
return;
}
disablePrivateKeyEncryption(function(err) {
$rootScope.$emit('Local/NewEncryptionSetting');
if (err) {
$scope.encryptEnabled = true;
$log.error(err);
return;
}
$scope.encryptEnabled = false;
});
});
}
} }
}; };
$scope.touchidChange = function() { $scope.touchIdChange = function() {
var newStatus = $scope.touchIdEnabled;
var opts = { walletService.setTouchId(wallet, newStatus, function(err) {
touchIdFor: {}
};
opts.touchIdFor[walletId] = $scope.touchidEnabled;
fingerprintService.check(wallet, function(err) {
if (err) { if (err) {
$log.debug(err); $log.warn(err);
$timeout(function() { $scope.touchIdEnabled = !newStatus;
$scope.touchidError = true;
$scope.touchidEnabled = true;
}, 100);
return; return;
} }
configService.set(opts, function(err) { $log.debug('Touch Id status changed: ' + newStatus);
if (err) {
$log.debug(err);
$scope.touchidError = true;
$scope.touchidEnabled = false;
}
});
}); });
}; };
}); });

View file

@ -30,14 +30,6 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
]; ];
$scope.selectedPriceSensitivity = $scope.priceSensitivity[1]; $scope.selectedPriceSensitivity = $scope.priceSensitivity[1];
var handleEncryptedWallet = function(client, cb) {
if (!walletService.isEncrypted(client)) return cb();
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
if (err) return cb(err);
return cb(walletService.unlock(client, password));
});
};
this.init = function(testnet) { this.init = function(testnet) {
self.allWallets = profileService.getWallets(testnet ? 'testnet' : 'livenet', 1); self.allWallets = profileService.getWallets(testnet ? 'testnet' : 'livenet', 1);
@ -263,66 +255,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
this.confirmTx = function(txp, cb) { this.confirmTx = function(txp, cb) {
fingerprintService.check(client, function(err) { // TODO see walletService createAndPublish
if (err) {
$log.debug(err);
return cb(err);
}
handleEncryptedWallet(client, function(err) {
if (err) {
$log.debug(err);
return cb(err);
}
ongoingProcess.set('Sending Bitcoin to Coinbase...', true);
walletService.publishTx(client, txp, function(err, publishedTxp) {
if (err) {
ongoingProcess.set('Sending Bitcoin to Coinbase...', false);
$log.debug(err);
return cb({
errors: [{
message: 'Transaction could not be published: ' + err.message
}]
});
}
walletService.signTx(client, publishedTxp, function(err, signedTxp) {
walletService.lock(client);
if (err) {
ongoingProcess.set('Sending Bitcoin to Coinbase...', false);
$log.debug(err);
walletService.removeTx(client, signedTxp, function(err) {
if (err) $log.debug(err);
});
return cb({
errors: [{
message: 'The payment was created but could not be completed: ' + err.message
}]
});
}
walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
if (err) {
ongoingProcess.set('Sending Bitcoin to Coinbase...', false);
$log.debug(err);
walletService.removeTx(client, broadcastedTxp, function(err) {
if (err) $log.debug(err);
});
return cb({
errors: [{
message: 'The payment was created but could not be broadcasted: ' + err.message
}]
});
}
$timeout(function() {
return cb(null, broadcastedTxp);
}, 5000);
});
});
});
});
});
}; };
}); });

View file

@ -208,53 +208,44 @@ angular.module('copayApp.controllers').controller('sellGlideraController',
self.error = err.message ||  bwcError.msg(err); self.error = err.message ||  bwcError.msg(err);
return; return;
} }
fingerprintService.check(wallet, function(err) { walletService.prepare(wallet, txp, function(err, password) {
if (err) { if (err) {
self.error = err.message ||  bwcError.msg(err); self.error = err.message ||  bwcError.msg(err);
return; return;
} }
ongoingProcess.set('signingTx', true);
walletService.handleEncryptedWallet(wallet, function(err) { walletService.publishTx(wallet, createdTxp, function(err, publishedTxp) {
if (err) { if (err) {
ongoingProcess.clear();
self.error = err.message ||  bwcError.msg(err); self.error = err.message ||  bwcError.msg(err);
return;
} }
ongoingProcess.set('signingTx', true); walletService.signTx(wallet, publishedTxp, function(err, password, signedTxp) {
walletService.publishTx(wallet, createdTxp, function(err, publishedTxp) { walletService.removeTx(wallet, signedTxp, function(err) {
if (err) $log.debug(err);
});
ongoingProcess.clear();
if (err) { if (err) {
ongoingProcess.clear();
self.error = err.message ||  bwcError.msg(err); self.error = err.message ||  bwcError.msg(err);
return;
} }
var rawTx = signedTxp.raw;
walletService.signTx(wallet, publishedTxp, function(err, signedTxp) { var data = {
walletService.lock(wallet); refundAddress: refundAddress,
walletService.removeTx(wallet, signedTxp, function(err) { signedTransaction: rawTx,
if (err) $log.debug(err); priceUuid: self.sellPrice.priceUuid,
}); useCurrentPrice: self.sellPrice.priceUuid ? false : true,
ip: null
};
ongoingProcess.set('Seling Bitcoin', true);
glideraService.sell(token, twoFaCode, data, function(err, data) {
ongoingProcess.clear(); ongoingProcess.clear();
if (err) { if (err) {
self.error = err.message ||  bwcError.msg(err); self.error = err.message ||  bwcError.msg(err);
return; return;
} }
var rawTx = signedTxp.raw; self.success = data;
var data = { $scope.update();
refundAddress: refundAddress,
signedTransaction: rawTx,
priceUuid: self.sellPrice.priceUuid,
useCurrentPrice: self.sellPrice.priceUuid ? false : true,
ip: null
};
ongoingProcess.set('Seling Bitcoin', true);
glideraService.sell(token, twoFaCode, data, function(err, data) {
ongoingProcess.clear();
if (err) {
self.error = err.message ||  bwcError.msg(err);
return;
}
self.success = data;
$scope.update();
});
}); });
}); });
}); });

View file

@ -37,7 +37,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
walletService.getNotifications(wallet, { walletService.getNotifications(wallet, {
timeSpan: timeSpan timeSpan: timeSpan
}, function(err, n) { }, function(err, n) {
console.log('[tab-home.js.39]', wallet.name, n); //TODO
if (err) { if (err) {
console.log('[tab-home.js.35:err:]', $log.error(err)); //TODO console.log('[tab-home.js.35:err:]', $log.error(err)); //TODO
return; return;

View file

@ -87,11 +87,6 @@ angular.module('copayApp.services')
}); });
}); });
if (wallet.hasPrivKeyEncrypted() && !wallet.isPrivKeyEncrypted()) {
$log.warn('Auto locking unlocked wallet:' + walletId);
wallet.lock();
}
wallet.initialize({}, function(err) { wallet.initialize({}, function(err) {
if (err) { if (err) {
$log.error('Could not init notifications err:', err); $log.error('Could not init notifications err:', err);
@ -548,14 +543,6 @@ angular.module('copayApp.services')
return cb(gettext('Could not import. Check input file and spending password')); return cb(gettext('Could not import. Check input file and spending password'));
} }
if (walletClient.hasPrivKeyEncrypted()) {
try {
walletClient.disablePrivateKeyEncryption();
} catch (e) {
$log.warn(e);
}
}
str = JSON.parse(str); str = JSON.parse(str);
var addressBook = str.addressBook || {}; var addressBook = str.addressBook || {};

View file

@ -547,27 +547,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return isEncrypted; return isEncrypted;
}; };
root.lock = function(wallet) {
try {
wallet.lock();
} catch (e) {
$log.warn('Encrypting wallet:', e);
};
};
root.unlock = function(wallet, password) {
if (lodash.isEmpty(wallet))
return 'MISSING_PARAMETER';
if (lodash.isEmpty(password))
return 'NO_PASSWORD_GIVEN';
try {
wallet.unlock(password);
} catch (e) {
$log.warn('Decrypting wallet:', e);
return 'PASSWORD_INCORRECT';
}
};
root.createTx = function(wallet, txp, cb) { root.createTx = function(wallet, txp, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet)) if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER'); return cb('MISSING_PARAMETER');
@ -578,28 +557,12 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
else return cb(null, createdTxp); else return cb(null, createdTxp);
}); });
} else { } else {
wallet.getFeeLevels(wallet.credentials.network, function(err, levels) { wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err); if (err) return cb(err);
else {
var feeLevelValue = lodash.find(levels, { $log.debug('Transaction created');
level: txp.feeLevel return cb(null, createdTxp);
}); }
if (!feeLevelValue || !feeLevelValue.feePerKB)
return cb({
message: 'Could not get dynamic fee for level: ' + feeLevel
});
$log.debug('Dynamic fee: ' + txp.feeLevel + ' ' + feeLevelValue.feePerKB + ' SAT');
txp.feePerKb = feeLevelValue.feePerKB;
wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err);
else {
$log.debug('Transaction created');
return cb(null, createdTxp);
}
});
}); });
} }
}; };
@ -619,8 +582,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}); });
}; };
root.signTx = function(wallet, txp, cb) { root.signTx = function(wallet, txp, password, cb) {
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet)) if (!wallet || !txp || !cb)
return cb('MISSING_PARAMETER'); return cb('MISSING_PARAMETER');
if (wallet.isPrivKeyExternal()) { if (wallet.isPrivKeyExternal()) {
@ -637,8 +600,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
} else { } else {
try { try {
wallet.signTxProposal(txp, function(err, signedTxp) { wallet.signTxProposal(txp, password, function(err, signedTxp) {
$log.debug('Transaction signed'); $log.debug('Transaction signed err:' + err);
return cb(err, signedTxp); return cb(err, signedTxp);
}); });
} catch (e) { } catch (e) {
@ -861,12 +824,12 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// An alert dialog // An alert dialog
var askPassword = function(name, cb) { var askPassword = function(name, title, cb) {
var scope = $rootScope.$new(true); var scope = $rootScope.$new(true);
scope.data = []; scope.data = [];
var pass = $ionicPopup.show({ var pass = $ionicPopup.show({
template: '<input type="password" ng-model="data.pass">', template: '<input type="password" ng-model="data.pass">',
title: 'Enter Spending Password', title: title,
subTitle: name, subTitle: name,
scope: scope, scope: scope,
buttons: [{ buttons: [{
@ -891,12 +854,42 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}); });
}; };
root.encrypt = function(wallet, cb) {
askPassword(wallet.name, gettext('Enter new spending password'), function(password) {
if (!password) return cb('no password');
askPassword(wallet.name, gettext('Confirm you new spending password'), function(password2) {
if (!password2 || password != password2)
return cb('password mismatch');
wallet.encryptPrivateKey(password);
return cb();
});
});
};
root.decrypt = function(wallet, cb) {
$log.debug('Disabling private key encryption for' + wallet.name);
askPassword(wallet.name, gettext('Enter Spending Password'), function(password) {
if (!password) return cb('no password');
try {
wallet.decryptPrivateKey(password);
} catch (e) {
return cb(e);
}
return cb();
});
};
root.handleEncryptedWallet = function(wallet, cb) { root.handleEncryptedWallet = function(wallet, cb) {
if (!root.isEncrypted(wallet)) return cb(); if (!root.isEncrypted(wallet)) return cb();
askPassword(wallet.name, function(password) { askPassword(wallet.name, gettext('Enter Spending Password'), function(password) {
if (!password) return cb('no password'); if (!password) return cb('no password');
return cb(root.unlock(wallet, password));
return cb(null, password);
}); });
}; };
@ -932,6 +925,19 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}); });
}; };
root.prepare = function(wallet, cb) {
fingerprintService.check(wallet, function(err) {
if (err) return cb(err);
root.handleEncryptedWallet(wallet, function(err, password) {
if (err) return cb(err);
return cb(null, password);
});
});
};
root.publishAndSign = function(wallet, txp, cb) { root.publishAndSign = function(wallet, txp, cb) {
var publishFn = root.publishTx; var publishFn = root.publishTx;
@ -943,56 +949,51 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}; };
} }
fingerprintService.check(wallet, function(err) { root.prepare(wallet, function(err, password) {
if (err) return cb(err); if (err) return cb('Prepare error: ' + err);
root.handleEncryptedWallet(wallet, function(err) { ongoingProcess.set('sendingTx', true);
if (err) return cb(err); publishFn(wallet, txp, function(err, publishedTxp) {
ongoingProcess.set('sendingTx', false);
if (err) return cb('Send Error: ' + err);
ongoingProcess.set('sendingTx', true); ongoingProcess.set('signingTx', true);
publishFn(wallet, txp, function(err, publishedTxp) { root.signTx(wallet, publishedTxp, password, function(err, signedTxp) {
ongoingProcess.set('sendingTx', false); ongoingProcess.set('signingTx', false);
if (err) return cb(err); root.invalidateCache(wallet);
ongoingProcess.set('signingTx', true);
root.signTx(wallet, publishedTxp, function(err, signedTxp) {
root.lock(wallet);
ongoingProcess.set('signingTx', false);
root.invalidateCache(wallet);
if (err) { if (err) {
// TODO? $log.warn('sign error:' + err);
var msg = err.message ? // TODO?
err.message : var msg = err.message ?
gettext('The payment was created but could not be completed. Please try again from home screen'); err.message :
$rootScope.$emit('Local/TxAction', wallet.id); gettext('The payment was created but could not be completed. Please try again from home screen');
return cb(err); $rootScope.$emit('Local/TxAction', wallet.id);
} return cb(msg);
}
if (signedTxp.status == 'accepted') { if (signedTxp.status == 'accepted') {
ongoingProcess.set('broadcastingTx', true); ongoingProcess.set('broadcastingTx', true);
root.broadcastTx(wallet, signedTxp, function(err, broadcastedTxp) { root.broadcastTx(wallet, signedTxp, function(err, broadcastedTxp) {
ongoingProcess.set('broadcastingTx', false); ongoingProcess.set('broadcastingTx', false);
if (err) return cb(err); if (err) return cb('sign error' + err);
var type = txStatus.notify(broadcastedTxp); var type = txStatus.notify(broadcastedTxp);
root.openStatusModal(type, broadcastedTxp, function() { root.openStatusModal(type, broadcastedTxp, function() {
$rootScope.$emit('Local/TxAction', wallet.id);
});
return cb(null, broadcastedTxp)
});
} else {
var type = txStatus.notify(signedTxp);
root.openStatusModal(type, signedTxp, function() {
root.invalidateCache(wallet);
$rootScope.$emit('Local/TxAction', wallet.id); $rootScope.$emit('Local/TxAction', wallet.id);
}); });
return cb(null, signedTxp);
} return cb(null, broadcastedTxp)
}); });
} else {
var type = txStatus.notify(signedTxp);
root.openStatusModal(type, signedTxp, function() {
root.invalidateCache(wallet);
$rootScope.$emit('Local/TxAction', wallet.id);
});
return cb(null, signedTxp);
}
}); });
}); });
}); });
@ -1004,7 +1005,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (err) return cb(err); if (err) return cb(err);
notifications = lodash.filter(notifications, function(x) { notifications = lodash.filter(notifications, function(x) {
return x.type != 'NewBlock' && x.type != 'BalanceUpdated' && x.type !='NewOutgoingTxByThirdParty'; return x.type != 'NewBlock' && x.type != 'BalanceUpdated' && x.type != 'NewOutgoingTxByThirdParty';
}); });
var idToName = {}; var idToName = {};
@ -1027,42 +1028,34 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.getEncodedWalletInfo = function(wallet, cb) { root.getEncodedWalletInfo = function(wallet, cb) {
var getCode = function() { var derivationPath = wallet.credentials.getBaseAddressDerivationPath();
var derivationPath = wallet.credentials.getBaseAddressDerivationPath(); var encodingType = {
var encodingType = { mnemonic: 1,
mnemonic: 1, xpriv: 2,
xpriv: 2, xpub: 3
xpub: 3 };
}; var info;
var info;
// not supported yet // not supported yet
if (wallet.credentials.derivationStrategy != 'BIP44' || !wallet.canSign()) if (wallet.credentials.derivationStrategy != 'BIP44' || !wallet.canSign())
return null; return null;
if (wallet.credentials.mnemonic) { root.getKeys(wallet, function(err, keys){
if (err || !keys) return cb(err);
if (keys.mnemonic) {
info = { info = {
type: encodingType.mnemonic, type: encodingType.mnemonic,
data: wallet.credentials.mnemonic, data: keys.mnemonic,
} }
} else { } else {
info = { info = {
type: encodingType.xpriv, type: encodingType.xpriv,
data: wallet.credentials.xPrivKey data: keys.xPrivKey
} }
} }
return info.type + '|' + info.data + '|' + wallet.credentials.network.toLowerCase() + '|' + derivationPath + '|' + (wallet.credentials.mnemonicHasPassphrase); return cb(null, info.type + '|' + info.data + '|' + wallet.credentials.network.toLowerCase() + '|' + derivationPath + '|' + (wallet.credentials.mnemonicHasPassphrase));
};
fingerprintService.check(wallet, function(err) {
if (err) return cb(err);
root.handleEncryptedWallet(wallet, function(err) {
if (err) return cb(err);
var code = getCode();
return cb(null, code);
});
}); });
}; };
@ -1110,7 +1103,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
} }
}); });
// messsages... // messages...
var u = bwcService.getUtils(); var u = bwcService.getUtils();
lodash.each(finale, function(x) { lodash.each(finale, function(x) {
@ -1124,6 +1117,31 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}; };
root.setTouchId = function(wallet, enabled, cb) {
fingerprintService.check(wallet, function(err) {
if (err) return cb(err); {
$log.debug(err);
return;
}
configService.set(opts, cb);
});
};
root.getKeys = function(wallet, cb) {
root.prepare(wallet, function(err, password) {
if (err) return cb(err);
var keys;
try {
keys = wallet.getKeys(password);
} catch (e) {
return cb(err);
}
return cb(null, keys);
});
};
return root; return root;
}); });

View file

@ -1,6 +1,6 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Version=0.3.0 Version=0.6.0
Name=BitPay Name=BitPay
Comment=The BitPay Bitcoin Wallet Comment=The BitPay Bitcoin Wallet
Exec=bitpay Exec=bitpay

View file

@ -2,7 +2,7 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "bitpay" #define MyAppName "bitpay"
#define MyAppVersion "0.3.0" #define MyAppVersion "0.6.0"
#define MyAppPublisher "BitPay" #define MyAppPublisher "BitPay"
#define MyAppURL "https://bitpay.com" #define MyAppURL "https://bitpay.com"
#define MyAppExeName "*NAMECASENOSPACE.exe" #define MyAppExeName "*NAMECASENOSPACE.exe"