diff --git a/js/controllers/copayers.js b/js/controllers/copayers.js index 73bf09330..8896e7566 100644 --- a/js/controllers/copayers.js +++ b/js/controllers/copayers.js @@ -35,8 +35,8 @@ angular.module('copayApp.controllers').controller('CopayersController', }; $scope.deleteWallet = function() { - var w = $rootScope.wallet; - identity.delete(w.id, function() { + var w = $rootScope.iden; + iden.deleteWallet(w.id, function() { controllerUtils.logout(); }); }; diff --git a/js/controllers/createProfile.js b/js/controllers/createProfile.js new file mode 100644 index 000000000..7fc321fe4 --- /dev/null +++ b/js/controllers/createProfile.js @@ -0,0 +1,24 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('CreateProfileController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) { + controllerUtils.redirIfLogged(); + + $scope.createProfile = function(form) { + + $scope.loading = true; + copay.Identity.create(form.email.$modelValue, form.password.$modelValue, { + pluginManager: pluginManager, + network: config.network, + networkName: config.networkName, + walletDefaults: config.wallet, + passphrase: config.passphrase, + }, function(err, iden ,w) { + $scope.loading = false; + $rootScope.iden = iden; + $rootScope.wallet = w; + + controllerUtils.bindWallet(w, $scope); + }); + } + +}); diff --git a/js/models/Identity.js b/js/models/Identity.js index d026c3ea1..05bb82df7 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -30,7 +30,8 @@ function Identity(email, password, opts) { if (opts.pluginManager) { storageOpts = _.clone({ - db: opts.pluginManager.get('DB') + db: opts.pluginManager.get('DB'), + passphrase: opts.passphrase, }); /* * TODO (plugins for other services) @@ -43,6 +44,7 @@ function Identity(email, password, opts) { storageOpts.password = password; this.storage = Identity._newStorage(storageOpts); + this.storage.setPassword(password); this.networks = { 'livenet': Identity._newAsync(opts.network.livenet), @@ -57,7 +59,7 @@ function Identity(email, password, opts) { this.version = opts.version || version; // open wallets - this.wallets = []; + this.openWallets = []; }; @@ -118,11 +120,12 @@ Identity.create = function(email, password, opts, cb) { cb(null, iden); // default wallet - var wopts = _.extend(opts.walletDefaults,{ + var wopts = _.extend(opts.walletDefaults, { nickname: email, networkName: opts.networkName, requiredCopayers: 1, totalCopayers: 1, + password: password, }); iden.createWallet(wopts, function(err, w) { return cb(null, iden, w); @@ -192,11 +195,11 @@ Identity.prototype.store = function(opts, cb) { self.profile.store(opts, function(err) { if (err) return cb(err); - var l = self.wallets.length, + var l = self.openWallets.length, i = 0; if (!l) return cb(); - _.each(self.wallets, function(w) { + _.each(self.openWallets, function(w) { w.store(function(err) { if (err) return cb(err); @@ -207,6 +210,28 @@ Identity.prototype.store = function(opts, cb) { }); }; + +/** + * @desc Closes the wallet and disconnects all services + */ +Identity.prototype.close = function(cb) { + preconditions.checkState(this.profile); + + var l = self.openWallets.length, + i = 0; + if (!l) return cb(); + + _.each(self.openWallets, function(w) { + w.close(function(err) { + if (err) return cb(err); + + if (++i == l) + return cb(); + }) + }); +}; + + /** * @desc Imports a wallet from an encrypted base64 object * @param {string} base64 - the base64 encoded object @@ -214,10 +239,10 @@ Identity.prototype.store = function(opts, cb) { * @param {string[]} skipFields - fields to ignore when importing * @return {Wallet} */ -Identity.prototype.importWallet = function(base64, passphrase, skipFields, cb) { +Identity.prototype.importWallet = function(base64, password, skipFields, cb) { preconditions.checkArgument(cb); - this.storage.setPassphrase(passphrase); + this.storage.setPassword(password); var obj = this.storage.decrypt(base64); if (!obj) return false; @@ -241,7 +266,7 @@ Identity.prototype.importWallet = function(base64, passphrase, skipFields, cb) { * @param {number} opts.totalCopayers * @param {PublicKeyRing=} opts.publicKeyRing * @param {string} opts.nickname - * @param {string} opts.passphrase + * @param {string} opts.password * @TODO: Figure out what is this parameter * @param {?} opts.spendUnconfirmed this.walletDefaults.spendUnconfirmed ?? * @TODO: Figure out in what unit is this reconnect delay. @@ -300,7 +325,7 @@ Identity.prototype.createWallet = function(opts, cb) { opts.totalCopayers = totalCopayers; opts.version = opts.version || this.version; - this.storage.setPassphrase(opts.passphrase); + this.storage.setPassword(opts.password); var self = this; var w = Identity._newWallet(opts); @@ -320,10 +345,10 @@ Identity.prototype.addWallet = function(wallet, cb) { preconditions.checkState(this.profile); var self = this; - self.profile.addWallet(wallet.id, function(err) { - if (err) return cb(err); + self.profile.addWallet(wallet.id, {}, function(err) { - self.wallets.push(wallet); + if (err) return cb(err); + self.openWallets.push(wallet); wallet.store(function(err) { return cb(err); }); @@ -356,16 +381,16 @@ Identity.prototype._checkVersion = function(inVersion) { /** * @desc Retrieve a wallet from the storage * @param {string} walletId - the id of the wallet - * @param {string} passphrase - the passphrase to decode it + * @param {string} password - the password to decode it * @param {function} callback (err, {Wallet}) * @return */ -Identity.prototype.openWallet = function(walletId, passphrase, cb) { +Identity.prototype.openWallet = function(walletId, password, cb) { preconditions.checkArgument(cb); var self = this; - self.storage.setPassphrase(passphrase); - self.migrateWallet(walletId, passphrase, function() { + self.storage.setPassword(password); + self.migrateWallet(walletId, password, function() { Identity._walletRead(walletId, self.storage, self.networks, self.blockchains, [], function(err, w) { if (err) return cb(err); @@ -428,7 +453,7 @@ Identity.prototype.decodeSecret = function(secret) { * * @param {object} opts * @param {string} opts.secret - the wallet secret - * @param {string} opts.passphrase - a passphrase to use to encrypt the wallet for persistance + * @param {string} opts.password - a password to use to encrypt the wallet for persistance * @param {string} opts.nickname - a nickname for the current user * @param {string} opts.privateHex - the private extended master key * @param {walletCreationCallback} cb - a callback @@ -436,7 +461,7 @@ Identity.prototype.decodeSecret = function(secret) { Identity.prototype.joinWallet = function(opts, cb) { preconditions.checkArgument(opts); preconditions.checkArgument(opts.secret); - preconditions.checkArgument(opts.passphrase); + preconditions.checkArgument(opts.password); preconditions.checkArgument(opts.nickname); preconditions.checkArgument(cb); var self = this; @@ -495,7 +520,7 @@ Identity.prototype.joinWallet = function(opts, cb) { walletOpts.privateKey = privateKey; walletOpts.nickname = opts.nickname; - walletOpts.passphrase = opts.passphrase; + walletOpts.password = opts.password; self.createWallet(walletOpts, function(err, w) { diff --git a/js/models/Profile.js b/js/models/Profile.js index 4c857c2c1..69113c5cf 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -8,7 +8,7 @@ function Profile(info, storage) { preconditions.checkArgument(info.email); preconditions.checkArgument(info.hash); preconditions.checkArgument(storage); - preconditions.checkArgument(storage.setPassphrase, 'bad storage'); + preconditions.checkArgument(storage.setPassword, 'bad storage'); this.hash = info.hash; this.email = info.email; @@ -30,7 +30,7 @@ Profile.key = function(hash) { Profile.create = function(email, password, storage, cb) { preconditions.checkArgument(cb); - preconditions.checkArgument(storage.setPassphrase); + preconditions.checkArgument(storage.setPassword); preconditions.checkState(storage.hasPassphrase()); @@ -97,6 +97,8 @@ Profile.prototype.addToWallet = function(walletId, info, cb) { Profile.prototype.addWallet = function(walletId, info, cb) { + preconditions.checkArgument(cb); + if (this.walletInfos[walletId]) return cb(new Error('WEXIST: Wallet already on profile')); @@ -111,7 +113,7 @@ Profile.prototype.addWallet = function(walletId, info, cb) { }; -Profile.prototype.setLasOpenedTs = function(walletId, cb) { +Profile.prototype.setLastOpenedTs = function(walletId, cb) { return this.addToWallet(walletId, { lastOpenedTs: Date.now() }, cb); @@ -129,7 +131,7 @@ Profile.prototype.store = function(opts, cb) { return cb(new Error('PEXISTS: Profile already exist')) } else { self.storage.set(key, val, function(err) { - log.debug('Identity stored'); + log.debug('Profile stored'); if (cb) cb(err); }); diff --git a/js/models/Storage.js b/js/models/Storage.js index c74211d51..9612a7c3d 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -4,6 +4,7 @@ var CryptoJS = require('node-cryptojs-aes').CryptoJS; var bitcore = require('bitcore'); var Passphrase = require('./Passphrase'); var preconditions = require('preconditions').instance(); +var log = require('../log'); var _ = require('underscore'); var CACHE_DURATION = 1000 * 60 * 5; var id = 0; @@ -21,11 +22,7 @@ function Storage(opts) { this.wListCache = {}; this.__uniqueid = ++id; - this.passphraseConfig = { - salt: opts.salt, - iterations: opts.iterations, - }; - + this.passphraseConfig = opts.passphrase; this.setPassword(opts.password); try { @@ -61,7 +58,9 @@ Storage.prototype._setPassphrase = function(passphrase) { Storage.prototype.setPassword = function(password, config) { var passphraseConfig = _.extend(this.passphraseConfig, config); var p = new Passphrase(passphraseConfig); + log.debug('Setting passphrase... Iterations:' + (passphraseConfig.iterations || 'default')) this._setPassphrase(p.getBase64(password)); + log.debug('done.') } Storage.prototype._encrypt = function(string) { diff --git a/js/models/Wallet.js b/js/models/Wallet.js index fa96c8653..7c2bdd92a 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -875,7 +875,7 @@ Wallet.prototype.netStart = function() { copayerId: myId, privkey: myIdPriv, maxPeers: self.totalCopayers, - lastTimestamp: this.lastTimestamp, + lastTimestamp: this.lastTimestamp || 0, secretNumber: self.secretNumber, }; @@ -992,7 +992,7 @@ Wallet.prototype.toObj = function() { txProposals: this.txProposals.toObj(), privateKey: this.privateKey ? this.privateKey.toObj() : undefined, addressBook: this.addressBook, - lastTimestamp: this.lastTimestamp, + lastTimestamp: this.lastTimestamp || 0, }; return walletObj; @@ -1068,7 +1068,7 @@ Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { }); } - opts.lastTimestamp = o.lastTimestamp; + opts.lastTimestamp = o.lastTimestamp || 0; opts.storage = storage; opts.network = network; @@ -2530,7 +2530,7 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb) */ Wallet.prototype.close = function(cb) { var self = this; - log.debug('## CLOSING'); + log.debug('## CLOSING Wallet'); this.lock.release(function() { self.network.cleanUp(); self.blockchain.destroy(); diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index 5bf9aafe8..8bdc230c7 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -12,11 +12,11 @@ angular.module('copayApp.services') }; root.logout = function() { - if ($rootScope.wallet) - $rootScope.wallet.close(); + if ($rootScope.iden) + $rootScope.iden.close(); - $rootScope.wallet = null; delete $rootScope['wallet']; + delete $rootScope['iden']; // Clear rootScope for (var i in $rootScope) { @@ -154,7 +154,7 @@ angular.module('copayApp.services') }); }; - root.startNetwork = function(w, $scope) { + root.bindWallet = function(w, $scope) { root.setupRootVariables(); root.installWalletHandlers(w, $scope); root.updateAddressList(); diff --git a/test/PayPro.js b/test/PayPro.js index 9741225d3..c155311ed 100644 --- a/test/PayPro.js +++ b/test/PayPro.js @@ -58,7 +58,7 @@ describe('PayPro (in Wallet) model', function() { }); var storage = new Storage(walletConfig.storage); - storage.setPassphrase('xxx'); + storage._setPassphrase('xxx'); var network = new Network(walletConfig.network); var blockchain = new Blockchain(walletConfig.blockchain); c.storage = storage; diff --git a/test/Wallet.js b/test/Wallet.js index 740cf83fc..d4814c31a 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -80,7 +80,7 @@ describe('Wallet model', function() { }); var storage = new Storage(walletConfig.storage); - storage.setPassphrase('xxx'); + storage._setPassphrase('xxx'); var network = new Network(walletConfig.network); var blockchain = new Blockchain(walletConfig.blockchain); c.storage = storage; @@ -342,7 +342,7 @@ describe('Wallet model', function() { o.opts.reconnectDelay = 100; var s = new Storage(walletConfig.storage); - s.setPassphrase('xxx'); + s._setPassphrase('xxx'); var w2 = Wallet.fromObj(o, s, new Network(walletConfig.network), diff --git a/test/WalletLock.js b/test/WalletLock.js index 14b86409d..ed72bfbc0 100644 --- a/test/WalletLock.js +++ b/test/WalletLock.js @@ -10,7 +10,7 @@ describe('WalletLock model', function() { beforeEach(function() { storage = new Storage(requireMock('FakeLocalStorage').storageParams); - storage.setPassphrase('mysupercoolpassword'); + storage._setPassphrase('mysupercoolpassword'); storage.clearAll(); }); diff --git a/test/mocks/FakeLocalStorage.js b/test/mocks/FakeLocalStorage.js index 6fad99249..3a8428f43 100644 --- a/test/mocks/FakeLocalStorage.js +++ b/test/mocks/FakeLocalStorage.js @@ -32,5 +32,7 @@ module.exports.storageParams = { password: '123', db: new FakeLocalStorage(), sessionStorage: new FakeLocalStorage(), - iterations: 1, + passphrase: { + iterations: 1, + }, }; diff --git a/views/createProfile.html b/views/createProfile.html new file mode 100644 index 000000000..065b38cab --- /dev/null +++ b/views/createProfile.html @@ -0,0 +1,44 @@ +
+ +

( TODO1: only this form if there is any profile:: key) +

( TODO2: if user has wallets (wallet::) show message: Copay now needs a profile to ... , you can import your wallets after creating your profile ) +

+ + Retreiving information from storage... +
+
+
+ Copay +
+
+
+
+

Create Profile

+
+
+ + + + + + +
+
+ +
+
+
+ + + + +
+ Back +
+
+
+
+ +