From eb9ab115d7ffcf94e93e4fb9062baaf5fcfc6b9f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 26 Sep 2014 05:00:43 -0300 Subject: [PATCH 001/182] rename walletFactory to identity. Test passing --- copay.js | 2 +- js/controllers/copayers.js | 4 +- js/controllers/create.js | 71 --- js/controllers/home.js | 4 +- js/controllers/import.js | 4 +- js/controllers/join.js | 7 +- js/controllers/more.js | 4 +- js/controllers/open.js | 6 +- js/models/{WalletFactory.js => Identity.js} | 38 +- js/services/identity.js | 5 + js/services/walletFactory.js | 5 - test/test.Identity.js | 609 ++++++++++++++++++++ test/unit/services/servicesSpec.js | 4 +- util/build.js | 4 +- 14 files changed, 652 insertions(+), 115 deletions(-) delete mode 100644 js/controllers/create.js rename js/models/{WalletFactory.js => Identity.js} (92%) create mode 100644 js/services/identity.js delete mode 100644 js/services/walletFactory.js create mode 100644 test/test.Identity.js diff --git a/copay.js b/copay.js index dc1ff87de..0ea40a3fd 100644 --- a/copay.js +++ b/copay.js @@ -13,7 +13,7 @@ var Async = module.exports.Async = require('./js/models/Async'); var Insight = module.exports.Insight = require('./js/models/Insight'); var Storage = module.exports.Storage = require('./js/models/Storage'); -module.exports.WalletFactory = require('./js/models/WalletFactory'); +module.exports.Identity = require('./js/models/Identity'); module.exports.Wallet = require('./js/models/Wallet'); module.exports.WalletLock = require('./js/models/WalletLock'); module.exports.PluginManager = require('./js/models/PluginManager'); diff --git a/js/controllers/copayers.js b/js/controllers/copayers.js index 43721afb7..73bf09330 100644 --- a/js/controllers/copayers.js +++ b/js/controllers/copayers.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('CopayersController', - function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils) { + function($scope, $rootScope, $location, backupService, identity, controllerUtils) { $scope.isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; $scope.hideAdv = true; @@ -36,7 +36,7 @@ angular.module('copayApp.controllers').controller('CopayersController', $scope.deleteWallet = function() { var w = $rootScope.wallet; - walletFactory.delete(w.id, function() { + identity.delete(w.id, function() { controllerUtils.logout(); }); }; diff --git a/js/controllers/create.js b/js/controllers/create.js deleted file mode 100644 index 77059bb92..000000000 --- a/js/controllers/create.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -angular.module('copayApp.controllers').controller('CreateController', - function($scope, $rootScope, $location, $timeout, walletFactory, controllerUtils, Passphrase, backupService, notification, defaults) { - controllerUtils.redirIfLogged(); - - $rootScope.fromSetup = true; - $scope.loading = false; - $scope.walletPassword = $rootScope.walletPassword; - $scope.isMobile = !!window.cordova; - $scope.hideAdv = true; - $scope.networkName = config.networkName; - $scope.networkUrl = config.network[$scope.networkName].url; - - // ng-repeat defined number of times instead of repeating over array? - $scope.getNumber = function(num) { - return new Array(num); - } - - $scope.totalCopayers = config.wallet.totalCopayers; - $scope.TCValues = _.range(1, config.limits.totalCopayers + 1); - - var updateRCSelect = function(n) { - var maxReq = copay.Wallet.getMaxRequiredCopayers(n); - $scope.RCValues = _.range(1, maxReq + 1); - $scope.requiredCopayers = Math.min(parseInt(n / 2 + 1), maxReq); - }; - - updateRCSelect($scope.totalCopayers); - - $scope.$watch('totalCopayers', function(tc) { - updateRCSelect(tc); - }); - - $scope.$watch('networkName', function(tc) { - $scope.networkUrl = config.network[$scope.networkName].url; - }); - - $scope.showNetwork = function(){ - return $scope.networkUrl != defaults.network.livenet.url && $scope.networkUrl != defaults.network.testnet.url; - }; - - $scope.create = function(form) { - if (form && form.$invalid) { - notification.error('Error', 'Please enter the required fields'); - return; - } - $scope.loading = true; - Passphrase.getBase64Async($scope.walletPassword, function(passphrase) { - var opts = { - requiredCopayers: $scope.requiredCopayers, - totalCopayers: $scope.totalCopayers, - name: $scope.walletName, - nickname: $scope.myNickname, - passphrase: passphrase, - privateKeyHex: $scope.private, - networkName: $scope.networkName, - }; - walletFactory.create(opts, function(err, w) { - controllerUtils.startNetwork(w, $scope); - }); - }); - }; - - $scope.isSetupWalletPage = 0; - - $scope.setupWallet = function() { - $scope.isSetupWalletPage = !$scope.isSetupWalletPage; - }; - - }); diff --git a/js/controllers/home.js b/js/controllers/home.js index 23119ded1..29e736589 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -1,11 +1,11 @@ 'use strict'; -angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, walletFactory, notification, controllerUtils) { +angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, identity, notification, controllerUtils) { controllerUtils.redirIfLogged(); $scope.retreiving = true; - walletFactory.getWallets(function(err,ret) { + identity.getWallets(function(err,ret) { $scope.retreiving = false; $scope.hasWallets = (ret && ret.length > 0) ? true : false; }); diff --git a/js/controllers/import.js b/js/controllers/import.js index 4c10279e4..9b6de0a91 100644 --- a/js/controllers/import.js +++ b/js/controllers/import.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('ImportController', - function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase, notification, isMobile) { + function($scope, $rootScope, $location, identity, controllerUtils, Passphrase, notification, isMobile) { controllerUtils.redirIfLogged(); $scope.title = 'Import a backup'; @@ -30,7 +30,7 @@ angular.module('copayApp.controllers').controller('ImportController', // try to import encrypted wallet with passphrase try { - w = walletFactory.import(encryptedObj, passphrase, skipFields); + w = identity.import(encryptedObj, passphrase, skipFields); } catch (e) { errMsg = e.message; } diff --git a/js/controllers/join.js b/js/controllers/join.js index fffd2e86b..f6f5e5d66 100644 --- a/js/controllers/join.js +++ b/js/controllers/join.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('JoinController', - function($scope, $rootScope, $timeout, walletFactory, controllerUtils, Passphrase, notification) { + function($scope, $rootScope, $timeout, identity, controllerUtils, Passphrase, notification) { controllerUtils.redirIfLogged(); $rootScope.fromSetup = false; $scope.loading = false; @@ -121,12 +121,13 @@ angular.module('copayApp.controllers').controller('JoinController', $scope.loading = true; Passphrase.getBase64Async($scope.joinPassword, function(passphrase) { - walletFactory.joinCreateSession({ + identity.joinCreateSession({ secret: $scope.connectionId, nickname: $scope.nickname, passphrase: passphrase, privateHex: $scope.private, }, function(err, w) { + $scope.loading = false; if (err || !w) { if (err === 'joinError') @@ -137,8 +138,6 @@ angular.module('copayApp.controllers').controller('JoinController', notification.error('Network Error', 'Wallet network configuration missmatch'); else if (err === 'badSecret') notification.error('Bad secret', 'The secret string you entered is invalid'); - else if (err === 'connectionError') - notification.error('Networking Error', 'Could not connect to the Insight server. Check your settings and network configuration'); else notification.error('Unknown error'); controllerUtils.onErrorDigest(); diff --git a/js/controllers/more.js b/js/controllers/more.js index 6b4fb4f51..c251bb7b5 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('MoreController', - function($scope, $rootScope, $location, $filter, backupService, walletFactory, controllerUtils, notification, rateService) { + function($scope, $rootScope, $location, $filter, backupService, identity, controllerUtils, notification, rateService) { var w = $rootScope.wallet; $scope.isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; @@ -81,7 +81,7 @@ angular.module('copayApp.controllers').controller('MoreController', }; $scope.deleteWallet = function() { - walletFactory.delete(w.id, function() { + identity.delete(w.id, function() { controllerUtils.logout(); }); }; diff --git a/js/controllers/open.js b/js/controllers/open.js index e14663319..278e2d889 100644 --- a/js/controllers/open.js +++ b/js/controllers/open.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('OpenController', function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase, notification) { +angular.module('copayApp.controllers').controller('OpenController', function($scope, $rootScope, $location, identity, controllerUtils, Passphrase, notification) { controllerUtils.redirIfLogged(); if ($rootScope.pendingPayment) { @@ -16,7 +16,7 @@ angular.module('copayApp.controllers').controller('OpenController', function($sc $scope.loading = false; $scope.retreiving = true; - walletFactory.getWallets(function(err, wallets) { + identity.getWallets(function(err, wallets) { if (err || !wallets || !wallets.length) { $location.path('/'); @@ -48,7 +48,7 @@ angular.module('copayApp.controllers').controller('OpenController', function($sc Passphrase.getBase64Async(password, function(passphrase) { var w, errMsg; - walletFactory.open($scope.selectedWalletId, passphrase, function(err, w) { + identity.open($scope.selectedWalletId, passphrase, function(err, w) { if (!w) { $scope.loading = false; notification.error('Error', err.errMsg || 'Wrong password'); diff --git a/js/models/WalletFactory.js b/js/models/Identity.js similarity index 92% rename from js/models/WalletFactory.js rename to js/models/Identity.js index 7db1a0890..a65abbcaf 100644 --- a/js/models/WalletFactory.js +++ b/js/models/Identity.js @@ -15,7 +15,7 @@ var Storage = module.exports.Storage = require('./Storage'); /** * @desc - * WalletFactory - stores the state for a wallet in creation + * Identity - stores the state for a wallet in creation * * @param {Object} config - configuration for this wallet * @@ -35,7 +35,7 @@ var Storage = module.exports.Storage = require('./Storage'); * @constructor */ -function WalletFactory(config, version, pluginManager) { +function Identity(config, version, pluginManager) { var self = this; preconditions.checkArgument(config); preconditions.checkArgument(config.network); @@ -73,7 +73,7 @@ function WalletFactory(config, version, pluginManager) { * @param {Object} wallet object * @return {string} network name */ -WalletFactory.prototype.obtainNetworkName = function(obj) { +Identity.prototype.obtainNetworkName = function(obj) { return obj.networkName || obj.opts.networkName || obj.publicKeyRing.networkName || @@ -86,7 +86,7 @@ WalletFactory.prototype.obtainNetworkName = function(obj) { * @param {string[]} skipFields - fields to skip when importing * @return {Wallet} */ -WalletFactory.prototype.fromObj = function(inObj, skipFields) { +Identity.prototype.fromObj = function(inObj, skipFields) { var networkName = this.obtainNetworkName(inObj); preconditions.checkState(networkName); preconditions.checkArgument(inObj); @@ -118,7 +118,7 @@ WalletFactory.prototype.fromObj = function(inObj, skipFields) { * @param {string[]} skipFields - fields to ignore when importing * @return {Wallet} */ -WalletFactory.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) { +Identity.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) { this.storage.setPassphrase(passphrase); var walletObj = this.storage.import(base64); if (!walletObj) return false; @@ -127,19 +127,19 @@ WalletFactory.prototype.fromEncryptedObj = function(base64, passphrase, skipFiel /** * @TODO: import is a reserved keyword! DONT USE IT - * @TODO: this is essentialy the same method as {@link WalletFactory#fromEncryptedObj}! + * @TODO: this is essentialy the same method as {@link Identity#fromEncryptedObj}! * @desc Imports a wallet from an encrypted base64 object * @param {string} base64 - the base64 encoded object * @param {string} passphrase - passphrase to decrypt it * @param {string[]} skipFields - fields to ignore when importing * @return {Wallet} */ -WalletFactory.prototype.import = function(base64, passphrase, skipFields) { +Identity.prototype.import = function(base64, passphrase, skipFields) { var self = this; return self.fromEncryptedObj(base64, passphrase, skipFields); }; -WalletFactory.prototype.migrateWallet = function(walletId, passphrase, cb) { +Identity.prototype.migrateWallet = function(walletId, passphrase, cb) { var self = this; self.storage.setPassphrase(passphrase); @@ -168,7 +168,7 @@ WalletFactory.prototype.migrateWallet = function(walletId, passphrase, cb) { * @param {string[]} skipFields - parameters to ignore when importing * @param {function} callback - {err, Wallet} */ -WalletFactory.prototype.read = function(walletId, skipFields, cb) { +Identity.prototype.read = function(walletId, skipFields, cb) { var self = this, err; var obj = {}; @@ -199,7 +199,7 @@ WalletFactory.prototype.read = function(walletId, skipFields, cb) { }); }; -WalletFactory.prototype.read_Old = function(walletId, skipFields, cb) { +Identity.prototype.read_Old = function(walletId, skipFields, cb) { var self = this, err; var obj = {}; @@ -238,7 +238,7 @@ WalletFactory.prototype.read_Old = function(walletId, skipFields, cb) { */ -WalletFactory.prototype._getWallet = function(opts) { +Identity.prototype._getWallet = function(opts) { return new Wallet(opts); }; @@ -262,7 +262,7 @@ WalletFactory.prototype._getWallet = function(opts) { * @param {callback} opts.version * @return {Wallet} */ -WalletFactory.prototype.create = function(opts, cb) { +Identity.prototype.create = function(opts, cb) { preconditions.checkArgument(cb); opts = opts || {}; @@ -327,7 +327,7 @@ WalletFactory.prototype.create = function(opts, cb) { * @param {string} inVersion - a version, with major, minor, and revision, period-separated (x.y.z) * @throws {Error} if there's a major version difference */ -WalletFactory.prototype._checkVersion = function(inVersion) { +Identity.prototype._checkVersion = function(inVersion) { var thisV = this.version.split('.'); var thisV0 = parseInt(thisV[0]); var inV = inVersion.split('.'); @@ -349,7 +349,7 @@ WalletFactory.prototype._checkVersion = function(inVersion) { * @param {function} callback (err, {Wallet}) * @return */ -WalletFactory.prototype.open = function(walletId, passphrase, cb) { +Identity.prototype.open = function(walletId, passphrase, cb) { preconditions.checkArgument(cb); var self = this; self.storage.setPassphrase(passphrase); @@ -367,7 +367,7 @@ WalletFactory.prototype.open = function(walletId, passphrase, cb) { }); }; -WalletFactory.prototype.getWallets = function(cb) { +Identity.prototype.getWallets = function(cb) { var self = this; this.storage.getWallets(function(wallets) { wallets.forEach(function(i) { @@ -392,7 +392,7 @@ WalletFactory.prototype.getWallets = function(cb) { * @callback cb * @return {?} the result of the callback */ -WalletFactory.prototype.delete = function(walletId, cb) { +Identity.prototype.delete = function(walletId, cb) { var self = this; self.storage.deleteWallet(walletId, function(err) { if (err) return cb(err); @@ -405,7 +405,7 @@ WalletFactory.prototype.delete = function(walletId, cb) { /** * @desc Pass through to {@link Wallet#secret} */ -WalletFactory.prototype.decodeSecret = function(secret) { +Identity.prototype.decodeSecret = function(secret) { try { return Wallet.decodeSecret(secret); } catch (e) { @@ -434,7 +434,7 @@ WalletFactory.prototype.decodeSecret = function(secret) { * @param {string} opts.privateHex - the private extended master key * @param {walletCreationCallback} cb - a callback */ -WalletFactory.prototype.joinCreateSession = function(opts, cb) { +Identity.prototype.joinCreateSession = function(opts, cb) { preconditions.checkArgument(opts); preconditions.checkArgument(opts.secret); preconditions.checkArgument(opts.passphrase); @@ -513,4 +513,4 @@ WalletFactory.prototype.joinCreateSession = function(opts, cb) { }); }; -module.exports = WalletFactory; +module.exports = Identity; diff --git a/js/services/identity.js b/js/services/identity.js new file mode 100644 index 000000000..8cd37725e --- /dev/null +++ b/js/services/identity.js @@ -0,0 +1,5 @@ +'use strict'; +angular.module('copayApp.services').factory('identity', function(pluginManager){ + return new copay.Identity(config, copay.version, pluginManager); +}); + diff --git a/js/services/walletFactory.js b/js/services/walletFactory.js deleted file mode 100644 index a592470d2..000000000 --- a/js/services/walletFactory.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; -angular.module('copayApp.services').factory('walletFactory', function(pluginManager){ - return new copay.WalletFactory(config, copay.version, pluginManager); -}); - diff --git a/test/test.Identity.js b/test/test.Identity.js new file mode 100644 index 000000000..914a8e0c8 --- /dev/null +++ b/test/test.Identity.js @@ -0,0 +1,609 @@ +'use strict'; + + +var _ = require('underscore'); +var chai = chai || require('chai'); +var should = chai.should(); + +var copay = copay || require('../copay'); +var sinon = require('sinon'); +var FakeNetwork = require('./mocks/FakeNetwork'); +var FakeBlockchain = require('./mocks/FakeBlockchain'); +var FakeStorage = function FakeStorage() {}; +var Identity = require('../js/models/Identity'); +var Passphrase = require('../js/models/Passphrase'); +var mockLocalStorage = require('./mocks/FakeLocalStorage'); +var mockSessionStorage = require('./mocks/FakeLocalStorage'); + + +var PERSISTED_PROPERTIES = (copay.Wallet || require('../js/models/Wallet')).PERSISTED_PROPERTIES; + +function assertObjectEqual(a, b) { + PERSISTED_PROPERTIES.forEach(function(k) { + if (a[k] && b[k]) { + _.omit(a[k], 'name').should.be.deep.equal(b[k], k + ' differs'); + } + }) +} + + +describe('Identity model', function() { + + var wf; + + beforeEach(function() { + wf = new Identity(config, '0.0.1'); + + wf.storage.setPassphrase = sinon.spy(); + wf.storage.getSessionId = sinon.spy(); + wf.storage.setFromObj = sinon.spy(); + wf.storage.setLastOpened = sinon.stub().yields(null); + + + var w = sinon.stub(); + w.store = sinon.stub().yields(null); + + wf._getWallet = sinon.stub().returns(w); + }); + + + afterEach(function() { + wf = undefined; + }); + + + + var config = { + Network: FakeNetwork, + Blockchain: FakeBlockchain, + Storage: FakeStorage, + wallet: { + requiredCopayers: 3, + totalCopayers: 5, + spendUnconfirmed: 1, + reconnectDelay: 100, + + }, + blockchain: { + host: 'test.insight.is', + port: 80, + schema: 'https' + }, + networkName: 'testnet', + passphrase: { + iterations: 100, + storageSalt: 'mjuBtGybi/4=', + }, + + // network layer config + network: { + testnet: { + url: 'https://test-insight.bitpay.com:443' + }, + livenet: { + url: 'https://insight.bitpay.com:443' + }, + }, + + }; + + describe('#constructor', function() { + it('should create the factory', function() { + var wf = new Identity(config, '0.0.1'); + should.exist(wf); + wf.walletDefaults.should.deep.equal(config.wallet); + wf.version.should.equal('0.0.1'); + }); + }); + + // TODO this is a WALLET TEST! not Wallet Factory. Move it. + describe('#fromObj / #toObj', function() { + it('round trip', function() { + var wf = new Identity(config, '0.0.5'); + var original = JSON.parse(o); + var o2 = wf.fromObj(original).toObj(); + assertObjectEqual(o2, original); + }); + + it('round trip, using old copayerIndex', function() { + var wf = new Identity(config, '0.0.5'); + var w = wf.fromObj(JSON.parse(o)); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + assertObjectEqual(w.toObj(), JSON.parse(o)); + }); + + it('#fromObj, skipping fields', function() { + var wf = new Identity(config, '0.0.5'); + var w = wf.fromObj(JSON.parse(o), ['publicKeyRing']); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + (function() { + assertObjectEqual(w.toObj(), JSON.parse(o)) + }).should.throw(); + }); + + it('support old index schema: #fromObj #toObj round trip', function() { + var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; + var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; + + var wf = new Identity(config, '0.0.5'); + var w = wf.fromObj(JSON.parse(o)); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj); + should.exist(w.privateKey.toObj); + + assertObjectEqual(w.toObj(), JSON.parse(o2)); + }); + }); + + describe('#fromEncryptedObj', function() { + it('should create wallet from encrypted object', function() { + wf.storage.setPassphrase = sinon.spy(); + wf.storage.import = sinon.stub().withArgs('base64').returns('walletObj'); + wf.fromObj = sinon.stub().withArgs('walletObj').returns('ok'); + + var w = wf.fromEncryptedObj("encrypted object", "123"); + + w.should.equal('ok'); + wf.storage.setPassphrase.calledOnce.should.be.true; + wf.storage.setPassphrase.getCall(0).args[0].should.equal('123'); + wf.storage.import.calledOnce.should.be.true; + wf.fromObj.calledWith('walletObj').should.be.true; + }); + }); + + describe('#import', function() { + it('should import and update indexes', function() { + var wallet = { + id: "fake wallet", + updateIndexes: function(cb) { + cb(); + } + }; + wf.fromEncryptedObj = sinon.stub().returns(wallet); + + var w = wf.import("encrypted", "password"); + + should.exist(w); + wallet.should.equal(w); + }); + it('should import with a wrong password', function() { + wf.fromEncryptedObj = sinon.stub().returns(null); + var w = wf.import("encrypted", "passwordasdfasdf"); + should.not.exist(w); + }); + }); + + describe('#getWallets', function() { + it('should return empty array if no wallets', function(done) { + wf.storage.getWallets = sinon.stub().yields([]); + wf.storage.getLastOpened = sinon.stub().yields(null); + + wf.getWallets(function(err, ws) { + should.not.exist(err); + ws.should.deep.equal([]); + done(); + }); + }); + + it('should be able to get current wallets', function(done) { + wf.storage.getWallets = sinon.stub().yields([{ + name: 'w1', + id: 'id1', + }, { + name: 'w', + id: 'id2', + }]); + wf.storage.getLastOpened = sinon.stub().yields(null); + + wf.getWallets(function(err, ws) { + should.not.exist(err); + ws.should.deep.equal([{ + name: 'w1', + id: 'id1', + show: 'w1 ' + }, { + name: 'w', + id: 'id2', + show: 'w ' + }]); + done(); + }); + }); + it('should include last used info', function(done) { + wf.storage.getWallets = sinon.stub().yields([{ + name: 'w1', + id: 'id1', + }, { + name: 'w', + id: 'id2', + }]); + wf.storage.getLastOpened = sinon.stub().yields('id2'); + + wf.getWallets(function(err, ws) { + should.not.exist(err); + ws.should.deep.equal([{ + name: 'w1', + id: 'id1', + show: 'w1 ' + }, { + name: 'w', + id: 'id2', + lastOpened: true, + show: 'w ' + }]); + done(); + }); + }); + }); + + describe('#delete', function() { + it('should call deleteWallet', function(done) { + wf.storage.deleteWallet = sinon.stub().yields(null); + wf.delete('xxx', function() { + wf.storage.deleteWallet.getCall(0).args[0].should.equal('xxx'); + done(); + }); + }); + + it('should call lastOpened', function(done) { + wf.storage.deleteWallet = sinon.stub().yields(null); + wf.storage.setLastOpened = sinon.stub().yields(null); + wf.delete('xxx', function() { + wf.storage.setLastOpened.calledOnce.should.equal(true); + should.not.exist(wf.storage.setLastOpened.getCall(0).args[0]); + done(); + }); + }); + }); + + + describe('#read', function() { + it('should fail to read unexisting wallet', function(done) { + wf.storage.readWallet = sinon.stub().yields(null, {}); + + wf.read('id', [], function(err, w) { + should.not.exist(w); + should.exist(err); + should.exist(err.message); + var m = err.message.toString(); + m.should.to.have.string('Wallet not found'); + done(); + }); + }); + it('should fail to read broken wallet', function(done) { + wf.storage.readWallet = sinon.stub().yields(null, { + 'opts': 1 + }); + wf.read('id', [], function(err, w) { + should.not.exist(w); + should.exist(err); + should.exist(err.message); + var m = err.message.toString(); + m.should.to.have.string('Could not read'); + done(); + }); + }); + it('should read existing wallet', function(done) { + var wf = new Identity(config, '0.0.1'); + wf.storage.readWallet = sinon.stub().yields(null, { + 'opts': 1 + }); + wf.fromObj = sinon.stub().returns('ok'); + wf.read('id', [], function(err, w) { + should.not.exist(err); + should.exist(w); + done(); + }); + }); + }); + + + describe('#open', function() { + var opts = { + 'requiredcopayers': 2, + 'totalcopayers': 3 + }; + + it('should call setPassphrase', function(done) { + var wf = new Identity(config, '0.0.1'); + wf.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + wf.read = sinon.stub().yields(null, s1); + wf.migrateWallet = sinon.stub().yields(null); + wf.storage.setLastOpened = sinon.stub().yields(null); + + wf.open('dummy', 'xxx', function(err, w) { + wf.storage.setPassphrase.calledOnce.should.equal(true); + wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + done(); + }); + }); + + it('should call return wallet', function(done) { + var wf = new Identity(config, '0.0.1'); + wf.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + wf.read = sinon.stub().yields(null, s1); + wf.migrateWallet = sinon.stub().yields(null); + wf.storage.setLastOpened = sinon.stub().yields(null); + + wf.open('dummy', 'xxx', function(err, w) { + w.should.equal(s1); + s1.store.calledOnce.should.equal(true); + done(); + }); + }); + + + it('should call #store', function(done) { + var wf = new Identity(config, '0.0.1'); + wf.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + wf.read = sinon.stub().yields(null, s1); + wf.migrateWallet = sinon.stub().yields(null); + wf.storage.setLastOpened = sinon.stub().yields(null); + + wf.open('dummy', 'xxx', function(err, w) { + s1.store.calledOnce.should.equal(true); + done(); + }); + }); + + it('should call #setLastOpened', function(done) { + var wf = new Identity(config, '0.0.1'); + wf.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + wf.read = sinon.stub().yields(null, s1); + wf.migrateWallet = sinon.stub().yields(null); + wf.storage.setLastOpened = sinon.stub().yields(null); + + wf.open('dummy', 'xxx', function(err, w) { + wf.storage.setLastOpened.calledOnce.should.equal(true); + wf.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); + done(); + }); + }); + it('should call #migrateWallet', function(done) { + var wf = new Identity(config, '0.0.1'); + wf.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + wf.read = sinon.stub().yields(null, s1); + wf.migrateWallet = sinon.stub().yields(null); + wf.storage.deleteWallet_Old = sinon.stub().yields(null); + wf.storage.removeGlobal = sinon.stub().yields(null); + wf.storage.setLastOpened = sinon.stub().yields(null); + + wf.open('dummy', 'xxx', function(err, w) { + wf.migrateWallet.calledOnce.should.equal(true); + wf.migrateWallet.getCall(0).args[0].should.equal('dummy'); + done(); + }); + }); + }); + + describe('#create', function() { + it('should create wallet', function(done) { + wf.create(null, function(err, w) { + should.exist(w); + should.not.exist(err); + done(); + }); + }); + + it('should be able to create wallets with given pk', function(done) { + var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; + wf.create({ + privateKeyHex: priv, + }, function(err, w) { + wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + should.not.exist(err); + done(); + }); + }); + + it('should be able to create wallets with random pk', function(done) { + wf.create(null, function(err, w1) { + wf.create(null, function(err, w2) { + wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( + wf._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString + ); + done(); + }); + }); + }); + }); + + describe('#joinCreateSession', function() { + var opts = { + secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', + nickname: 'test', + passphrase: 'pass' + }; + + it('should yield bad network error', function(done) { + var net = wf.networks['testnet']; + net.greet = sinon.stub(); + net.on = sinon.stub(); + net.on.withArgs('data').yields('senderId', { + type: 'walletId', + networkName: 'aWeirdNetworkName', + opts: {}, + }); + opts.privHex = undefined; + wf.joinCreateSession(opts, function(err, w) { + err.should.equal('badNetwork'); + done(); + }); + }); + + + it('should yield to join error', function(done) { + opts.privHex = undefined; + var net = wf.networks['testnet']; + net.greet = sinon.stub(); + net.on = sinon.stub(); + net.on.withArgs('serverError').yields(null); + net.on.withArgs('data').yields('senderId', { + type: 'walletId', + networkName: wf.networkName, + }); + wf.joinCreateSession(opts, function(err, w) { + err.should.equal('joinError'); + done(); + }); + }); + + + it('should call network.start / create', function(done) { + opts.privHex = undefined; + var net = wf.networks['testnet']; + net.cleanUp = sinon.spy(); + net.greet = sinon.spy(); + net.start = sinon.stub().yields(null); + + net.on = sinon.stub(); + net.on.withArgs('connected').yields(null); + net.on.withArgs('data').yields('senderId', { + type: 'walletId', + networkName: 'testnet', + opts: {}, + }); + + var w = sinon.stub(); + w.sendWalletReady = sinon.spy(); + wf.create = sinon.stub().yields(null, w); + wf.joinCreateSession(opts, function(err, w) { + net.start.calledOnce.should.equal(true); + wf.create.calledOnce.should.equal(true); + wf.create.calledOnce.should.equal(true); + + w.sendWalletReady.calledOnce.should.equal(true); + w.sendWalletReady.getCall(0).args[0].should.equal('03ddbc4711534bc62ccf576ab05f2a0afd11f9e2f4016781f3f5a88de9543a229a'); + done(); + }); + }); + + it('should return walletFull', function(done) { + opts.privHex = undefined; + var net = wf.networks['testnet']; + net.cleanUp = sinon.spy(); + net.greet = sinon.spy(); + net.start = sinon.stub().yields(null); + + net.on = sinon.stub(); + net.on.withArgs('connected').yields(null); + net.on.withArgs('data').yields('senderId', { + type: 'walletId', + networkName: 'testnet', + opts: {}, + }); + wf.create = sinon.stub().yields(null, null); + wf.joinCreateSession(opts, function(err, w) { + err.should.equal('walletFull'); + done(); + }); + }); + + it('should accept a priv key a input', function() { + var wf = new Identity(config, '0.0.1'); + opts.privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd'; + var net = wf.networks['testnet']; + net.cleanUp = sinon.spy(); + net.start = sinon.spy(); + wf.joinCreateSession(opts, function(err, w) { + net.start.getCall(0).args[0].privkey.should.equal('ddc2fa8c583a73c4b2a24630ec7c283df4e7c230a02c4e48bc36ec61687afd7d'); + }); + }); + it('should call network.start with private key', function() { + opts.privHex = undefined; + var wf = new Identity(config, '0.0.1'); + var net = wf.networks['testnet']; + net.cleanUp = sinon.spy(); + net.start = sinon.spy(); + wf.joinCreateSession(opts, function(err, w) { + net.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer + }); + }); + }); + + + describe('Backwards compatibility tests', function() { + it('should be able to import unencrypted legacy wallet TxProposal: v0', function() { + var wf = new Identity(config, '0.0.5'); + var w = wf.fromObj(JSON.parse(legacyO)); + + should.exist(w); + w.id.should.equal('55d4bd062d32f90a'); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + }); + + it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function() { + + wf.storage.import = sinon.stub(); + wf.storage.setPassphrase = sinon.spy(); + wf.storage.import.withArgs('dummy').returns(JSON.parse(legacy1)); + + var w = wf.import('dummy', 'xxx'); + should.exist(w); + wf.storage.setPassphrase.calledOnce.should.equal(true); + wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + + w.isReady().should.equal(true); + var wo = w.toObj(); + wo.opts.id.should.equal('48ba2f1ffdfe9708'); + wo.opts.spendUnconfirmed.should.equal(true); + wo.opts.requiredCopayers.should.equal(1); + wo.opts.totalCopayers.should.equal(1); + wo.opts.name.should.equal('pepe wallet'); + wo.opts.version.should.equal('0.4.7'); + wo.publicKeyRing.walletId.should.equal('48ba2f1ffdfe9708'); + wo.publicKeyRing.networkName.should.equal('testnet'); + wo.publicKeyRing.requiredCopayers.should.equal(1); + wo.publicKeyRing.totalCopayers.should.equal(1); + wo.publicKeyRing.indexes.length.should.equal(2); + JSON.stringify(wo.publicKeyRing.indexes[0]).should.equal('{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1}'); + JSON.stringify(wo.publicKeyRing.indexes[1]).should.equal('{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}'); + wo.publicKeyRing.copayersBackup.length.should.equal(1); + wo.publicKeyRing.copayersBackup[0].should.equal('0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f'); + wo.publicKeyRing.copayersExtPubKeys.length.should.equal(1); + wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6'); + wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm'); + wo.privateKey.networkName.should.equal('testnet'); + + }); + }); +}); + + +var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; + +var legacyO = '{"opts":{"id":"55d4bd062d32f90a","spendUnconfirmed":true,"requiredCopayers":2,"totalCopayers":2,"name":"xcvzxcv","version":"0.3.2"},"networkNonce":"53d25e8600000009","networkNonces":[],"publicKeyRing":{"walletId":"55d4bd062d32f90a","networkName":"testnet","requiredCopayers":2,"totalCopayers":2,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":4,"receiveIndex":2},{"copayerIndex":1,"changeIndex":5,"receiveIndex":2}],"copayersBackup":["02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5"],"copayersExtPubKeys":["tpubD94LTzAUiW99mpA59nyf6fAHh4xKGmnwbgCV4gU2bRpeN9CRiMSurqme22px5NmJAo6FdcdH883Zu98VbqyhesCJ86kUEjH3Zpufy5FfcaC","tpubDA2U9H6LkRHDRbRxHBp4VTbxPc7JqsvtcLxrE5QJF8z1iT6hMJ1pXSVf57GWRcxXutYvpoXRurDVGsscJauMtnJBkYAWBVExYmm91XQE2zz"],"nicknameFor":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":"asdf","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":"qwerqw"},"publicKeysCache":{"m/0/0/0":["028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90","0332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec8"],"m/1/0/0":["0220ad514cf593d0c3905d3bb49bc5767a9410823bf9b77ea5ef2cf1d1016d77a8","02fd42cf66f1dbdc7bbb9ae09aecea72df479ffe5a0c4641301067e331d12e416d"],"m/1/0/1":["0315f7868eaf1f9b7127e3f7e0222c5e473eea003e34700f4758b6873c525d6723","02a2e8ed5e90dd39e3842fc790e06178997dbca319987f365317589e2a71a93658"],"m/0/1/0":["0244a25a0b97b26707fd855c15b046b901be85a3b70a781d0678608e633440eeca","0358cdcbc528ddfb7173b0dab283f702be82546ff031e4a832a7270080cb875959"],"m/0/1/1":["025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f","020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d"],"m/1/1/0":["02fd0e7c62b7b58d1ea7bb4cb84d53b019df99d3703a42aed73a2cfa15f3af5d08","0355a15912e76072ef50e6643376b8a9da8422ed4f8ea07b1d84d4989be5a39b2e"],"m/1/1/1":["03bc3e1f4db32efd8eb1fd44a1665938d59628429c67e1e8b7054ab5717f4e6750","03c4c817b633ac31f44f16f390af831d35f7d98744a52a0f23e9598967342255f8"],"m/1/1/2":["02826fe7e9da408480ddeb1d4414c5100b350f862ca718e27122681e1a0ca35077","02bd25af907bb3edbf6b2cd1ea90eaa92cc93ec47bea7d339af44c1d2c05708e99"],"m/0/1/2":["0337a1a70364b94745d6e26d2d28919cf528304f52765f12ef43e3d6da0a6c8dc0","039d83db9aa43e6e00e0304e6971b6079d79dc12d8d55ce2e6fc24a52ba8d41329"],"m/0/0/1":["0359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b8138","037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d93"],"m/1/1/3":["02600e5c41670773a213a4cb58c8f2fa3e83840784bc7f0b56925e1075e06632c2","036d01867af5f61371151ef7d9026fa0400a623f6924e404ee0b856625268972f9"],"m/0/1/3":["03e5a9b039b187ca8e065627df402e4a5b196b94198542da7036879de08be63d2d","0304f3e0b70f696d80e5785dc7747d6dcb55ba24c31f2d80bf184b4e582e6b47fc"],"m/1/1/4":["03741afa5bd50d6ba5801064c810fae84f6a4557d6a88ddc8591d0d4eb68a8fc41","0214dd6ce6073b05999fb887098ca6f7e1d0b4fdc0760557786907df353df90d1c"],"m/2147483647/1/0":["033e072a53ea835763a03c66e35c35384736210a1bb7d7ee6d9a3e109e82426b30","02e37b5570c053da8a8ee587be86fc629775c4db890aba2745ccc4e4dcc8c31041"],"m/2147483647/1/1":["0228a6de42ef421c263d1efd9f28d9a7d15a261995028a24eff6b9f1c3fc46e6bf","0226cff885cb0d607cc9cf69a7608316eb3fb2ec344c0c9956246ba776116fc396"],"m/2147483647/1/2":["034fe2a8f0b98445eb5810fe36572ad2f64ed9bf64dc9de624f99c0142cb07c682","02f2c5c758e32293f5c193fd69afadbba83abafb397db01e6f2b447690e900475a"],"m/2147483647/1/3":["02b25ef9434446c51f10678f787e4913de582e34d164bd3b06af7732c5476df1a8","025d51a1efd59bcff22ee2e0af61b21a7ba5f639e20dfdf25690e926005177dd0c"],"m/2147483647/1/4":["03e5734e1d29b2f684d0446b7a2ffbd0ba8952570a502d0d14b1efd8f24b61be53","0258fc28a324848d8d0154e8614815e35c668d274a8f01957bb99aab8dc8f386c0"],"m/2147483647/1/5":["021f9e775246765e1cfba0ae453b4eae6cd4ae5a57a09c319edbe89d4dbbf23be3","02857f66571a1c3eb9e72d22ae88e734c03d448bced4dcfd345c2059468124c741"],"m/2147483647/1/6":["02c072f329391a25255dc6452e5f5220966869dbf736ba8a8c3ae9d273a84bc3fd","030920a8b8e88c4db2871a7df0878a86cf0695f6d96bb50c701c3454f3df25176a"],"m/2147483647/1/7":["036bf329fc19bce10cf1999fae5bfa80290ff7b44776b49c7b0dc9eec6cffcfa21","03955a549875b4f7b9be28b9ff4bcd51ad2bc224430b1634baef890585885d5e1b"],"m/2147483647/1/8":["024879c9c9a261b3141ecfa1c79c4efc25278c844ecd1dcfcb95d9c19581fbdd25","03fb4a5fdb91239df3ccf7f61a5b99e7e72483101e21c9d1ee0d85544e9354c6c7"],"m/2147483647/1/9":["035928a107ec01f78cd586914d5a49710fd42e352b1312e3ad0eeb2c9666fdf8e7","03a54c03093797854829c75357f092356352a109042bbb83bdac20cb4e5eca27ea"],"m/2147483647/1/10":["021e7a3a7efe888c5e820b5cf0f03317b2b4bf438d8563449aeb7a77cade97f136","03ec0960b3d1df52ca3cc2c82b7d97063400da4dd051bba2f9bab6cb44aee01efa"],"m/2147483647/1/11":["035d70c26b7f429861f555f7c0d99947411b23b7f95303fb8d5de5b82a95aa30fc","038b922f7024f5446d6b48e5253643543b35c006d90fd37688105c6cefcd8adb8a"],"m/2147483647/1/12":["02158d6503891c6c65a606221dbf5c68d0832288975914007968419939588ecb24","0248264cb1763a3f4de9b34787b4bc5443ec92ef915927494bb9f1c1c0b498c7ca"],"m/2147483647/1/13":["0349965eea38a25ae0c061faeac4c4e57e648bc4c0f059d07b3b8b7962cbc0dde5","0352243d9269565ce2a1ffdd0b8e43a442c6dd1c9edda86eaaf2cba5a4a95c40f1"],"m/2147483647/1/14":["030fa6e3d0c5cedc0581955395c77cbe134c912a47971023b9695332df3f7bb200","03f2cf09e33326fb59bf3f13e6298d2d5d29c9eae3b872e5a851e8d8d77259c883"],"m/2147483647/1/15":["02bf0d45e41339f552df6f8baf4392142921fd38b0f2a4388a905ff6cbacbc278a","03fabe46bb6706a1b8edfd28c046a8891b4530bbe5305080b72b0d08ebdf7b8c0a"],"m/2147483647/1/16":["03a4e3146ed34d6a8af4e4379e6edcff32cb0373ba232b3d746af3052f674133ac","030311b73c6f5c46ddffc0cfce6e5ed0b671d94267d8e52cd8837f2a479916eb91"],"m/2147483647/1/17":["03233df93c762d2f06c7f5f388e4e0a8dbdb13302acba0d2d6995c487d8aec9f2f","024badfdcb7e772ac7fc1c46d3943b07500edbbece105cdeff3eb9e9fcc9f54782"],"m/2147483647/1/18":["0364035475a098e00eb010c500cad3c90af3e81a4bd613144bc9433a150f14718b","028223dc8142154e7477ce000b3dc13e1d15a901553d9b18864c8645b582b38fe6"],"m/2147483647/1/19":["03971b74b4ac4bdaadf636baa4caa82fe5355471ed6ea05a9cbe5fc6c9e4b9db76","0202ebffacd01f83849e5bc5c0e2c317bc5fb2fbcb2d6d4482a5235f9f1308b61a"],"m/0/1/4":["03005ee9ff028c98fd132e531023f2f2b61ff0d26022f979dd98088d2ba167b031","0345ea82e8dfe38277f0c3aee18d2dd93edb63e8663ac83328a7934d2ca57006f6"],"m/0/1/5":["0391bc4990b71d8a3f156ae7107929ed6372b0b4ba8a868253f71ba7189d1efa02","0312a74cf2e7c0dd41897d04fabfd8cc3187b84a28305cfc79315b24e6fe23a6b4"],"m/0/1/6":["021a38c492607ff9684a4fec445e47b5b7100d3ef9e9dc0d0b37c0a646d28d4f77","03ae0b46ab36f97447ebaa53f2b5c8f090f15395378785f2fd285eeba17fbf3f65"],"m/0/1/7":["0308cdec88c1ffe16edc98853d9c08dbd4ba2541ba566668ca17bda19d7eb3481f","02dd622267c2e68287287b8b61724f76fbe84096a56aa5054af92f8fe25380e2d1"],"m/0/1/8":["039647da9ad725836bcb28a3e0497659a28d7749d1416c421a0a01c62d237ee962","022e22aa61eafda0dd8820427f1a06314d352a15ea8645e7ab9b80920017084d82"],"m/0/1/9":["03a4ade946076c6962b70c70ac7fad3a87efb59a1d0a4e32bda13a6d47fe9df961","029a07235aba04ab69526e117d836d5b3fae5cfc8c5e72b10c6d1afd261ccc19f3"],"m/0/1/10":["03c78e9b6493b22790db1acea20df9444e0f9c424fc5756e7a32c290ae01783953","0254c130ee467a96570c9f5ebea89de04f0b1db1686b164f2694339bef8f25dd88"],"m/0/1/11":["03a762c43318ef8d4840fab04c8db73797dc648825fac60f2730b4c76678df1cf3","0212c684a4de8e750ad2dfe2b136370ab9803eca178ed9a27b3990c29b067de35c"],"m/0/1/12":["02702d221f9b15c5cf75ac2f497a6c63e60213087c3d2d3be46768e3ebd238e26e","03ed58580744deb357258e44548212038670769d8d51e385d4fb8414311fd01b52"],"m/0/1/13":["0320e0597b54c62768352f433389cee4725d6094d7bcb5c72265edcc0933829aff","02c5706f11b9a85f3176c572842b7c9812c2195058d24d945bc026b00312740e76"],"m/0/1/14":["02fe43077676b844226d3aaa62e8a86d237710d92f882366944acbde0c8992fcaf","039a6a8662abb8910741cf331320549665e9feb28ca94d1ab6a43c84fa330b94ee"],"m/0/1/15":["0369f99f72847af93d50ab8ee75b6e7e912d26e27be96f6d6b7215cf7daeff7ba5","02521700cc07c953ba5aa586fb0e4795a34dffc68c5fb43e038be3866e40f4daed"],"m/0/1/16":["02f67d1d89bd8fe2f91c5b973cbdacfb4ba440e7656bce284cf73d549625607347","035da9cfac5a803dcb2b283b02a2515a4a1bcbf3d19e0d180aee8fc30193bc0555"],"m/0/1/17":["02c024ec199d240e8d6c66276b94b91071f7cdf2bef540c29d6d18d25de7b1cf7c","02190865f9dafae3f7f05c093463be5632946422ddda0a6fef6904390792516067"],"m/0/1/18":["035ed504d7704ad984a333b8eb0fceb8be043da9284de31ed84d9e68d90c75507d","033303c415b50421732402df00f4baa219f334647a7eb5014b9f8079864d6ab558"],"m/0/1/19":["02ce49fe86b0eee73663b1ee867b16b97c876af26f12764c528a2e6d0eb55ad3d7","03ab969bc81796b88e44c340d854df955fc60ea17ea92db5d3115595d6dec890d8"],"m/0/1/20":["03e2fa915378cbdffa0d919b0fb50c7256ca731b9d571b3365e486893a1d43079c","038d058b895cf084dccfcc9367e4796a5cf4ddceed6c35f6885d75c80119613350"],"m/0/1/21":["02fcb1bf644446b5b42205272af72f0aeab9e92ca29aafa91c5fb69142764017aa","035c5fe5c8811603279a5b72b6c30735d702817db1eab937c622269e28192ffa90"],"m/0/1/22":["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5","022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"],"m/0/1/23":["03f40b82fe8cacff08879f13c45f443a3dc3ea98e1d75d5f32a19f5e5a8f7a905b","028415ee458e4dcfd440ce969726f3b58ae74fb6cf3995ced099579211e7419844"],"m/1/1/5":["032748a6282e21f571b8c8dd49e775deb83c90fcf88dc4ba81d878536973709c3f","020837cd68f14ce571b335eecd1b6fa0af43e1576dd9721aaca2a8ab639ac6b7cd"],"m/1/1/6":["0337032efb013dc92bb8dccfbdda9f5c28f0039a9c60953d41003d095e9f9778af","03ceed2da6b9603297061dc8eb930112ba726b2ccf5eec67f4866a05ca4049a22b"],"m/1/1/7":["0383c96ac2af7d203f69133b2fab6b68366b5075ad6957fa06759df3b20fbfec70","0311385f79834cedaf2230a48c0f9dc8e794da1869fc595db2518d62debb85579a"],"m/1/1/8":["03efc649680280f4e4df96da923bc88330275004125ebe5483c2f3e05ca52e19a4","02803c02d197d780388259afbd001ae41fa3eb3e2bac9627aff540521c184c3b23"],"m/1/1/9":["03af2fe6aa027a76b42c1c4050a040bfd026ad2daec1bb96a5fe2d026a7df919de","02ce14163047c640228796fb1f72bbe3afb05819ad141598a4f021058a6f79dd3b"],"m/1/1/10":["033770378bd762cf0408e44e4e604bef77e336170428c506949b1a4f1f2963e574","02c58ed43946f699dbd3e36d3e9aab2714cadeb19ecd3a56e4328c50336b4a76cb"],"m/1/1/11":["02898a1545fa19bdca92adc498698d27b86529cd4c08946d9d29604734b86f31af","02b402767a045ede072600924401c0d720000b2ed59fa444bfdbef4a5f1cead745"],"m/1/1/12":["039b8659430be49913e2cd869aa8c99ccf49a13df35837370b792033dadb891483","03264e63df292257cc76babb15d15bef620d1c2f8c3bbc78d6ea02d127e5ee7386"],"m/1/1/13":["02381a559791b8e86bf546e2c718ae63cf24eed0518a58e4d4a4b310adf2cd38fa","02d7f8283a4418d912508901b4a3db0d2103206dfdd74b3c75648671e20ecfd445"],"m/1/1/14":["020376e8c550b7d9faa0b2da947a2a36fab22c6e8190b6f99460b6022017bb97d7","03fbc5299190e6628de28c92aaa12e3a131b21eb7266462c46fbedeb86fa878055"],"m/1/1/15":["027209fd3b0cf7368180a5dbb16b928c997d33fccb78505d48440c7d23eadf5460","03450bfb22858726cd7e228e6733f69457546978a95188565c53e0d1c0d6070ea8"],"m/1/1/16":["03cb355ba04f64293793855121bab5831f84a3a3edf7cd31fccaa6d67c407a4912","028bc897a39c1224610b765a80f4cd8ab79cb37776f58fec9c10ac6f649d1f3c72"],"m/1/1/17":["03f4cb0564d7e2c6b85673503b7954db22779f29a8f3374904573984e318a96bf1","037c11b6ee906d84aa7eed359d758d986d912b6f8e5cbb1acf0982a77b3ef812c4"],"m/1/1/18":["02d2e5798f33f6889472857744316f2d253f25f88379610063f40cfe5798d9858f","0253cefdfe9ca987cbf1c950b6246d5b7a194d8dfad47c3a78dbbc5c1d01511d97"],"m/1/1/19":["0336c325f5aed366ffc10d553f2bfd4d69e66cbe1688d77af14efc8827aea2e318","0378b1b9a6074f9f2ab4fa9ad1e14649c621b0c8124a1b148914d3c10e6ab390c6"],"m/1/1/20":["03ea55740a734689ce778a8c00df8ebf4274c8f66de7d05646fe5c927773ff7f2e","02275b558d49aef955b6dee51a3c0a53f4b076b97bb3f26abcc82540168ec87cac"],"m/1/1/21":["03c77869c9984664eac9c238f4b6d806c9f48ca8a736c48450f398834db2aa915c","02d984f548c7f60c09dad3287cfc48807bc8157123989636c713be61be6a2e9ced"],"m/1/1/22":["03ed7c6a3c854c1f9459891691cc32671402f9e47126919878251e568dbdf353f8","02a113dab22cd9e46967b3fd76b9b9ec1d227d88817a9300e42d332cca2a0877fd"],"m/1/1/23":["02ee186432dcf69fda50a6fdbd94651817d8a271c273a5b70cab3ec4ae77a3753b","02291370aad9de0dac676355ced64e268b0c431a51f42f12d13f5144940fce4285"],"m/1/1/24":["02bf71435e84e66547c8c583d5ba226a5ac4d935e0a9f9603ecd8925c3e847e91a","03578d8657d285a89d9d597632db662cfef9baccfb55c76b1e87948a94fc9de30d"],"m/2147483647/0/0":["02a8425bbe23426219065969f695a6c3e242b24e57226bffdd542be8fd6be968c9","03057a42fdb6569fb1615b173ccb702453db2eac5be4291b82d4511461eafbed87"],"m/2147483647/0/1":["0250c3d3e86e332010c5233c2ec3bc728026002f0037cb3382d6318409b0e70796","02cfac1e7c4c88191201080f8316af52d9faa6ba624a6e160279e9fac4d1cf79a9"],"m/2147483647/0/2":["02a8c266a5b92eb50c8be91f95e4d1ad968b2f57d527377fd642d63fb84474f61a","028cc954ab31bd179ff80b8a05f95430ae534e61b3ff35f5284fa2fbe1832ceccb"],"m/2147483647/0/3":["02f719e1a7ab00ea98611453fb03d44c1da04655bed74af392534d70099039b4c2","03bfa548bfd4718c50bfce173f780eadcfb679d9c0206c91a2fa1879a9cf7558b2"],"m/2147483647/0/4":["0362c0695d397ca26bf47f0e641bb3cfb06ff29ccac2e1d56ded3afcf88b1e688d","02f9d87b05bdb3b9e82f506b43f813041c0e403274adc23d11e5e1651e34b606c2"],"m/2147483647/0/5":["033731323032d4ee08e858fc71f93970444333e183a1d5052e1d08cfb511e262c8","023e12556cef67ade35b7758916b5e1a3ebe074ccd35c5d8eff6b01321f63eb495"],"m/2147483647/0/6":["025d11b90081972bc1c258c9d6f476dfc2f95b69f0e9935322bf9c21deb580ff64","02b065f56a378907354f0738a0ed74f10660c6b5dd68c9f992093b75ce3d7d8b72"],"m/2147483647/0/7":["0210e721e8a35db9d8c855a0d346f60c09208f3be80b39e03af2c29db777332c71","0277f352969fadb1f1835f9a0fa99c6a3c7b6c281be5b2794c88a708eb177ea33d"],"m/2147483647/0/8":["02998d8d41e4215cd2a961a415a3ed0b1f984f1627719a7b102a75864943c4d87b","03d8ed7fc8f68a77f68d3afd007b7aa4c89944195143630ce183f0fa5438f2b559"],"m/2147483647/0/9":["0324fa91737588e4f85937303ce65c3b91b5f2ae506a72d92b83e3f5f9aeeb3c6f","02a011be72c4a400319212228106af278823a97acfe0a67e1ecd866d446b315114"],"m/2147483647/0/10":["025886ba287922a904881c7315e6fcc410a7976741771a5937d3a1a01b529f21fd","0243bb91ceed9d29d0c2ca66a8ab77e82110bbcc023beb4106f787964f44a0b972"],"m/2147483647/0/11":["0369d21684894cc2d4b2f5e581ede3cac9e8db4161a08e7737c1be129bb673d3d5","03c9ef27e3cd3dadc078fdfd9936a7ad9bf7954747085cf8f8a2a5bb3431f68a9f"],"m/2147483647/0/12":["03a73b8fd859bf6acebffdfffa2597199091daedd2c011ac67fc3494d8a1a8ceb6","025a213f7771c8be03f43f2e7f469ad4ef2cf6907ea284b227a786d1f55dfa7144"],"m/2147483647/0/13":["03a09f7ca257e1ab263cd5e6b0addc3ff868b93df132321d98775ca3505efb576f","03454c715739164bc55f347a651439cdf3ec146b35d2927beb60e8290b3916e082"],"m/2147483647/0/14":["03a64b1f7bd94a6b1a6e84ea444e0ba04e9deb86460934ccc37c0615a134a8257b","02794f09210b1811a455f3e1c7bcd35c76dff2523190fef9615eb27e2376acac1c"],"m/2147483647/0/15":["0392dca2fd9a3bc2b2a7d90a848719069fbc5f22bff7327bb8186c032514085263","032ee8a33ea76d70c7ae839448ca6c5b1af89146f2922e23ba1822df42dbc7e66a"],"m/2147483647/0/16":["031a22a1a3c1abad7c4d782ef6ba3cc00f2e8fe549eb33e0732200aff6d3174831","03bdce9781289e0c31cf727f4c93fe46f7930dd8fd68f818ce241f1ede268e8e0e"],"m/2147483647/0/17":["03b12d27e9aea2c2ad598e54e40860a705ac2ca2427aa511b501b38ec368ea5c7d","03e60d35d84d4536cad895215256b312bb4879a8d417251c279995e58f25da3d54"],"m/2147483647/0/18":["0380266cc9a9673676ad6a1b2e7148766df9c25b4dce299e5edc4f65b72aa58e64","0329e2a8a48c06c0c45dfdd2ab33e6455551557d8ebaf8c12fdf7470f8c45f1d28"],"m/2147483647/0/19":["036fe62af85560d7eea7c7af55e60b32a97dca80134d0aedffb19eb2705b9d6e01","02381c2c30b9f81e2a53c69028fbe11803acad0420b267719b7a80870be0baaeb7"],"m/0/0/2":["027bf94b8fc4e9b42683af25fda125ccab8760040717d100270dd4afd032692daf","026382c6c9357250d96dc21e43c053857a64efeac1887fdcbc107fbe3ecfc6115a"],"m/0/0/3":["03fd203acbd9af3cbbfb709458f8952078234a36094f12d00372e4b2b14cfdf419","03f2e5db59aea5dc89f53ac2a9f4ef66d41265c45afc5d763e0ca61ab70c7c61ec"],"m/0/0/4":["02a1d7cf4fcdbbf4de4002b844c3bff1639073f1cd6e5c4a4e02596b45d3f518c2","03b5fba813294e6ae096ea158833453caa5a945609b0a554696091b9b152bb0f7d"],"m/0/0/5":["0261d37e3b56ef4e106c59753037f516a4b1c45e056b2a3e00f8b77f15aaa7f8a5","0256a55e66e0de1603f0d600c0eb5f5486cf3512a776a36f3ab0d1941fc0dc9b09"],"m/0/0/6":["031db2826af215fe6cbe3f6e121b0497840fc49be133cff0a4d4eab679d6b99d70","021dd722c3f35dd04fcdb57f09b76c723d521fb36751de03ffd08096ddf1dc1f86"],"m/0/0/7":["0354ea75bdd9eb5beae7262e4a5eeb58bd10103ee0185e85b749ea39f6615d0f62","03f2c8f3b6478c0501a8578d5caf5ac2974f8213fc5e699d62dd2af58fbe8781d4"],"m/0/0/8":["0282e67df3bcd1e1662469b4c3151fb50ee1e46b75d787d91184c16b9803131f82","02921a7054af1e425f4137a5eb6b34d1f2b9d81c2625230194bc30657bb4277e11"],"m/0/0/9":["033e7e387933983ceab37c8388bd8ebc5119760f493ffe6f083bef0e5dfe22891d","02d660d60cc55d80912e0745cb142a8596a4604fbf72f9aadec0599aa2ed62461a"],"m/0/0/10":["022ce5b2750ae34512199856eab9e912dc25281cd8b88e7688a46c3b9a389701cb","02f14aa1608fce3b6088148709eb5fe72b61699c931fa8d95a45fab1106859d1b0"],"m/0/0/11":["0288dbef3302c1bc5556028adb33e2f9e03c119dbad4f706befb8ce86cea459f2b","03f13ced465e2e0a3aaa8895f3185d5711e0bebdaf507610b7a669ac8fc82da8fe"],"m/0/0/12":["031ab4677885340d2f927ccc9747f4346b79e4eb6c750695095a8a2524610fa94a","038c881910fbd8b50d193db4e0c84f5b7840820397f92cf0718a8e06d027125503"],"m/0/0/13":["031b568452cba22eb7a88c6085489e53e35abd16068882e71a140e47e12dee9c61","020d09885ee362101d12d34ce0918d41593634db1b9413e5415c6755753b9330e8"],"m/0/0/14":["024177bc9aa03cfc72eda2dfddffd7fe9d0c2f007fc3ba1a48280feae2b9fb117a","03394ad321668440c08da76eb35475ba3a8c0e8cbe0ed81468673a8c72d38fe457"],"m/0/0/15":["02037b1cc696ffbe9eba3684edd53653386ef6cd7728401c40120037593a4c2ae2","020ab8d6900ec9c11ca5d96dfc0ce7cf0ee71653a7c45118e89abb4b113147e53a"],"m/0/0/16":["023bcbb8d4726a546087cdb83740adf0ace879b7195a572c652fa8ce4dbe195a04","0392721b230d5163d28b27fc7e059b875711f12b3da448eabe7229bde57530e637"],"m/0/0/17":["02498ee74e849d3e9261dd1863038caf83d6a3bc2eeebecf17055d4bab44dee77f","03d4dc104b2e0981693e8097437de9b05334a85e2c8edb02783897859bdbc93e32"],"m/0/0/18":["0218a9f524fe54abf8c3afd21314296cfd93eaa9227acbd457e6c9a742dc233cf4","03760f3d0c5db969bda698ff9352e3b7c332216c34825f4c6e857e39c9aee7cd35"],"m/0/0/19":["033dd51f7737f0e9db79f5c38e4298bf3396346904ef3933d290a22e5b77048d9e","0221b2eedccb9a37515263071550069b3b349a166f0f131d0028e8600d9a2251b9"],"m/0/0/20":["02cb6c39161f3244d7769f7ab96346cae2cf21cb6f4538f5e7382d363dc2f836c7","034f7bda4d1e9ed6a3774608a4d6cd8582ab59fe3187f8a7a7cf914d89426ebe28"],"m/0/0/21":["035490549d65f1360f10340037250b171470ff4c86966318a2b1eead6d8b969aea","03f6a04f6fcd07a4f32c82d53710ed30e0f54d43d41c67c661d158b3d0830c3ea2"],"m/1/0/2":["02972eae7e4302e319c266578e14a07839c1e788296a92906e6d66d938211dad5f","039ed6b488f1571ad6527acd6b6c5b8453eacf6665dc5cb7852e33d1c8ea73f9fe"],"m/1/0/3":["02bec4728888c2c045108353994bae5731ec7a7b41459023b0023e10b8d616bd30","03ce1efe16214c9eac595382e46a68143dd11a335b3f7c971ddd719ac544a5fc4b"],"m/1/0/4":["030e2df1d341568225d8dfbe5d07e98dae9f90e0f43e19dcc68c998a6ed7bcc1f0","0380f4c07dc84faf42d51779f104aa6e3b5c3ce2d7684b3cb76d49faeefc2b69d6"],"m/1/0/5":["029a54ddaa25f433b493f4b72df8c1d41be2c4d2963b8b61ee63cc86d16c12d066","021567c95e0317442e7367aa4e3378dd46c5bcef5860f789272fea83b917de0669"],"m/1/0/6":["03590320d80b61cc0874b579f467c9b5ccc50d9ef875bcf6bdd12e2d0c211e8973","03ee4677b6ee89a9d355851f2230506c6897ff219062c0df4ad9a85c60f3535f93"],"m/1/0/7":["03caf98ab1c9b79d1dc8029453a6137c08787b04043b79af3cb42d41d2d3f1338f","023f39ae4e2f4f3887d5fc58e0d3a0d7ee267dc04aa257c75b6b2d67d2f5580f81"],"m/1/0/8":["0352a2a3ea8209c9a2b633d788796ac2d16c08022440e04a77ab2835c7f971d266","0291bc248b3da997f35e8fae98a75a91fdac2819d74c4e270899338d48f7389e87"],"m/1/0/9":["02468d32d9c3c62418d506d4cd0da6cd2022d5bcafdb5f847cf7bde7a48ec6848b","032713d90d12eb6a072f3c1db6c0d3b680d3f78883016135fc0f78e8193d41d4b4"],"m/1/0/10":["034863cc6bab9b059be53413ba75c5fc286647c20d7f9e5512ef4754ea301dd1ce","03a33ab9c32a2264ee2464ebbb5892f0e34acf0fdede4f87395a89e9dacdd4930e"],"m/1/0/11":["031e19296695bfe8a96ba3bf58afa805ee1bd5471fddb3929b1678d69d442d69c9","0270feb33956fd9e937019d629523e26437493c0856514011e6aec88baf7721295"],"m/1/0/12":["03cce695d3c3843bf73e851b2446a77d7e235e5b80b4f4474f9946292eb8218742","039ea96c8822f0ec7ed28308d277f3e730480d7573579cd11b89aef4364cd9ffeb"],"m/1/0/13":["02ab4ac38eb405e822d12c0f0f354f04f9ee1d991dde887a5c1171096fe503158f","036809e60cae1203da8884ea1f85d4669ce6e053f8ba605d775e271b70ab4f6787"],"m/1/0/14":["039d61da23a8610fa0ee58eb37d7cea7ea9396c79153da97280ccf5e46718e3bac","03015c27bcc778682781fd6ad30aa6041db0b7e24270818cdceece0043ccc34b26"],"m/1/0/15":["03c088ed669132835d2728b0ecf294271c8388988c6ae264d43ca24f50e4005f81","03e2c118c9445a2ddc4c8afeb0ba49e21be3f818a483d346418b8922b8a371a2b7"],"m/1/0/16":["02bba7df9847f463c6b23eca37a4bd6efa3801a52b8ddfad804d902e783b70c81c","03764b657f23996e31c64a701facc1cbeb0c9edfdd605e2c1ed36cf48197565d45"],"m/1/0/17":["020445179c522295b89bf4bfd582eb03422e3fa20dcd29263925e9f44282d476d8","036e47bdd32f3061aed1c1f8c2a32b038c7b72391cb1f80ebfc150e58f88372766"],"m/1/0/18":["024d88c4bfcbba713d49e1edcd035234aaa1ee76ad7bcf75bf074a16658a6b0b6d","02b861e7a20d89f6875d2e44c78dbadb99503e282e5e60e9f65657af6fea81d425"],"m/1/0/19":["023a8ca9d5300181f157e1930d3b0800eebe7683d8df72e6cbf28834dbf1be5d60","026053c4f84c10d15890c0b254522972931bc2d5b7cdf9c1f9f3137c22edf3ecd3"],"m/1/0/20":["03137c66e9f3d61aba659f408d77a293fa0f3fea4ccb911074a681d6f61a55d023","0291aa1bbfbef59b16b0e37e185a706c589d448cb02e860c5df9c9d7242ecc739f"],"m/1/0/21":["03c08673e0cae55318bc9dcc4b5f11eb3ff71d42de04015e255dde3fd8cba7e09e","02423d4eab06cd5b26e71d145283523c011d58032700c517f00b328d2c90cf109f"]}},"txProposals":{"txps":[{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543144016,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543144645},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543170040},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/0"],"comment":"blablabla","builderObj":{"valueInSat":"29000000","valueOutSat":"8900000","feeSat":"10000","remainderSat":"20090000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":0,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a9010000009300493046022100ccbb8f398f74a76236629b8499ffc6f9518a2091f5a61a9a352c0a10f615961e022100b8f0769c76cf33bec3d7f81d9da2b74cf6e8a5e0a24ee5f48172854d8bcdbfa101475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff02a0cd8700000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac908c32010000000017a914560c292066792531164149c5ed63ad2793a61b928700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543188745,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543189341},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543206819},"rejectedBy":{},"sentTs":1405543207304,"sentTxid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","inputChainPaths":["m/45\'/0/0/0"],"comment":"que parece","builderObj":{"valueInSat":"29000000","valueOutSat":"9000000","feeSat":"10000","remainderSat":"19990000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":1,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a901000000da00483045022035423cc74824ba904907678dda3b62a20a787b96d1b3e9f3e9546f9c57f4e45902210080a1ff1c39f458ac1642b9e948bd62fd70563b5252e749cc8fc642cd763ee830014730440220524a13f36cfb03caa246d7d84de634ec9386f2c39c19bfa926037f48da86262b022050e58a6503d105ad2805f86806810a1aa7f20d6271e1340b42fa91ab6a30f3e801475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff0240548900000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf00531010000000017a9146130a9d51f996b7a1b9d3e10c80930834251909d8700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543505848,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"rejectedBy":{},"sentTs":1405543610315,"sentTxid":"6fe851b54b777a75fe80fa204dc674395e2af69efb1f7c0017e909eb82c3d914","inputChainPaths":["m/45\'/0/1/1"],"comment":"mandaaaaaaa","builderObj":{"valueInSat":"19990000","valueOutSat":"19980000","feeSat":"10000","remainderSat":"0","hashToScriptMap":{"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts":"5221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52ae"},"selectedUtxos":[{"address":"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts","txid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","vout":1,"ts":1405543157,"scriptPubKey":"a9146130a9d51f996b7a1b9d3e10c80930834251909d87","amount":0.1999,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001664f1a8f905e6a6a2ec5f63abd3520840e21541ea8eb4e72272edd9326c99b1601000000db0048304502206b18b3dba2646c552469d8ef52d7656f6a65f563032530f622abdfd8bd4c5cee022100e804b406eddebbc827646141e74dc64c76a770ed4e35183ffd35d265ad9f7d3b01483045022100f6c013638ff0a316b1baa93dfffba6a98cf3033c133e8bd899e933c9c3e47ce10220530f40e7ea52ae58bec695edbec6d566d2ee8e7b5f33f95e33093ad1e29a125401475221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52aeffffffff01e0de3001000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac00000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543781381,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543782017},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381},"rejectedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543794590},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"1","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c000000009200483045022064d877bc5171fbaef909c2a1a924e0023b3ccc0b530cb46653f06ecb230283e8022100bc6658d60ad4f7120d9226c8f6eada87f3b0388f73c458011988bab36e78ba15014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a91421c4a435d9ac263ec55b35a1a5ca95e979639b9b8700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543835343,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543835968},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543850998},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"2","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c0000000092004830450220302baae7de2e0f102bf3af2d5f450f673e51bd143020141a769ccdcdf16af188022100e7abc087c76050ed649e7139a5a136969e74e24a8d8f6223d3219ad033a26451014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a9148b102abba0729fb0690c61cf7187064d692d43d78700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543869803,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543870411},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543890406},"rejectedBy":{},"sentTs":1405543890913,"sentTxid":"6a0f61574ad65e537e7e99298968db565f97b894b61f4c8f8fac8fcaedb83e2b","inputChainPaths":["m/45\'/0/0/1"],"comment":"3","builderObj":{"valueInSat":"29000000","valueOutSat":"1100000","feeSat":"10000","remainderSat":"27890000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c00000000db00483045022100a8ce7907f9fd7dd41dd65c2dec425e008efea06ee7c80787c10c0e210fbf181302207712c0fdd1cb25836ac1fc2fd303c1e26b85e8980417719b9ed50e977a9693ec01483045022100d1780c4f028cd898920aca3eaceba352ed9306cd17f019ae2f634e8facad149a02203c84ab2093da8e22577e93f27a732f0728d4e6db0c749f3cd3d898d6a025152a014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff02e0c81000000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac5091a9010000000017a914cc1cab78458b1a951b91c6dcd7eeeeb682f506388700000000"}}],"walletId":"55d4bd062d32f90a","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPdWUAmaaopPftevC72Jtiu19V8ee5XijL9JvogqfR95uVrL85f8yBdQMq3KyQtG3Q91yWQb3XDbWWpcdWFDAmJ7Xy2XWkGJu","networkName":"testnet","privateKeyCache":{"m/45\'/0/0/0":"b6fd8d1a079efd523da34f31ba81f544fc3d0a728a8a98299d8980682518e79c","m/45\'/0/1/1":"0f4d52d2a99e4c8c1c2edf09fef12407c3abd2304b961198c3f131a8c8443a13","m/45\'/0/0/1":"de5c191c343bd6017b98708c03344849624a14e2c167cfd6eb8dcb075d139293"}},"addressBook":{"msj42CCGruhRsFrGATiUuh25dtxYtnpbTx":{"hidden":false,"createdTs":1405543109222,"copayerId":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","label":"faucet","signature":"3045022067576e5b37f2707a8dc66e57511ad9b10a3125bd95193fff6f8f6402969c3bf3022100adff9f417db07d88face13b3d13f422740d4421440cade1a205684dfdc5d733a"}}}'; + + + +var legacy1 = '{"opts":{"id":"48ba2f1ffdfe9708","spendUnconfirmed":true,"requiredCopayers":1,"totalCopayers":1,"name":"pepe wallet","version":"0.4.7"},"networkNonce":"5405f06b00000001","networkNonces":[],"publicKeyRing":{"walletId":"48ba2f1ffdfe9708","networkName":"testnet","requiredCopayers":1,"totalCopayers":1,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1},{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}],"copayersBackup":["0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f"],"copayersExtPubKeys":["tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"48ba2f1ffdfe9708","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm","networkName":"testnet"},"addressBook":{}}'; diff --git a/test/unit/services/servicesSpec.js b/test/unit/services/servicesSpec.js index dd2805a96..1e57c961b 100644 --- a/test/unit/services/servicesSpec.js +++ b/test/unit/services/servicesSpec.js @@ -10,8 +10,8 @@ beforeEach(angular.mock.module('copayApp')); describe("Unit: Walletfactory Service", function() { beforeEach(angular.mock.module('copayApp.services')); - it('should contain a walletFactory service', inject(function(walletFactory) { - expect(walletFactory).not.to.equal(null); + it('should contain a identity service', inject(function(identity) { + expect(identity).not.to.equal(null); })); }); diff --git a/util/build.js b/util/build.js index a842534f1..9732e1a92 100644 --- a/util/build.js +++ b/util/build.js @@ -58,8 +58,8 @@ var createBundle = function(opts) { expose: '../js/log' }); // b.external('bitcore'); - b.require('./js/models/WalletFactory', { - expose: '../js/models/WalletFactory' + b.require('./js/models/Identity', { + expose: '../js/models/Identity' }); b.require('./js/models/Wallet'); b.require('./js/models/Wallet', { From 691951a86af2edc4b9e07675b1c247a74294c443 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 11:38:25 -0300 Subject: [PATCH 002/182] refactor key handing in storage (part 1) --- js/models/Storage.js | 8 +++----- js/models/Wallet.js | 5 ++++- test/Storage.js | 18 +++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/js/models/Storage.js b/js/models/Storage.js index 3747971de..aa26ecbfb 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -330,12 +330,10 @@ Storage.prototype.getLastOpened = function(cb) { this.getGlobal('lastOpened', cb); }; -Storage.prototype.setFromObj = function(walletId, obj, cb) { +Storage.prototype.setFromObj = function(key, obj, cb) { + preconditions.checkArgument(key); preconditions.checkArgument(cb); - var self = this; - - var key = 'wallet::' + walletId + ((obj.opts && obj.opts.name) ? '_' + obj.opts.name : ''); - self._write(key, obj, function() { + this._write(key, obj, function() { return cb(); }); }; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index b9943a434..4477ef611 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -873,7 +873,10 @@ Wallet.prototype.keepAlive = function() { Wallet.prototype.store = function(cb) { var self = this; this.keepAlive(); - this.storage.setFromObj(this.id, this.toObj(), function(err) { + + var val = this.toObj(); + var key = 'wallet::' + this.id + ((val.opts && val.opts.name) ? '_' + obj.opts.name : ''); + this.storage.setFromObj(key, val, function(err) { log.debug('Wallet stored'); if (cb) cb(err); diff --git a/test/Storage.js b/test/Storage.js index a528044a8..e48de991d 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -177,8 +177,8 @@ describe('Storage model', function() { var w2 = { name: 'pepe' }; - s.setFromObj('1', w1, function() { - s.setFromObj('2', w2, function() { + s.setFromObj('wallet::1_wallet1', w1, function() { + s.setFromObj('wallet::2', w2, function() { s.getWallets2(function(ws) { ws[0].should.deep.equal({ id: '1', @@ -208,8 +208,8 @@ describe('Storage model', function() { name: 'pepe' }; - s.setFromObj('1', w1, function() { - s.setFromObj('2', w2, function() { + s.setFromObj('wallet::1_wallet1', w1, function() { + s.setFromObj('wallet::2', w2, function() { s._write('3::name', 'matias', function() { s._write('1::name', 'juan', function() { s.setGlobal('nameFor::3', 'wallet3', function() { @@ -280,8 +280,8 @@ describe('Storage model', function() { name: 'pepe' }; - s.setFromObj('1', w1, function() { - s.setFromObj('2', w2, function() { + s.setFromObj('wallet::1', w1, function() { + s.setFromObj('wallet::2', w2, function() { s.deleteWallet('3', function(err) { err.toString().should.include('WNOTFOUND'); done(); @@ -301,8 +301,8 @@ describe('Storage model', function() { name: 'pepe' }; - s.setFromObj('1', w1, function() { - s.setFromObj('2', w2, function() { + s.setFromObj('wallet::1', w1, function() { + s.setFromObj('wallet::2', w2, function() { s.deleteWallet('1', function(err) { should.not.exist(err); s.getWallets2(function(ws) { @@ -372,7 +372,7 @@ describe('Storage model', function() { describe('#setFromObj', function() { it('should store from an object as single key', function(done) { - s.setFromObj('id1', { + s.setFromObj('wallet::id1_nameid1', { 'key': 'val', 'opts': { 'name': 'nameid1' From 8d53fa82ed2f13d7728554d4d4a25d6555ae63c0 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 15:14:32 -0300 Subject: [PATCH 003/182] add Profile model --- js/models/Profile.js | 38 +++++++++++++++++++++++++++++ test/test.Profile.js | 57 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 js/models/Profile.js create mode 100644 test/test.Profile.js diff --git a/js/models/Profile.js b/js/models/Profile.js new file mode 100644 index 000000000..9665710c7 --- /dev/null +++ b/js/models/Profile.js @@ -0,0 +1,38 @@ +'use strict'; +var preconditions = require('preconditions').singleton(); +var _ = require('underscore'); +var log = require('../log'); +var bitcore = require('bitcore'); + +function Profile(opts, storage) { + preconditions.checkArgument(opts.email); + preconditions.checkArgument(opts.password); + preconditions.checkArgument(storage); + preconditions.checkArgument(storage.getItem); + + this.email = opts.email; + this.password = opts.password; + this.hash = bitcore.util.sha256ripe160(this.email + this.password); + + this.extra = opts.extra; +}; + +Profile.fromObj = function(obj, storage) { + return new Profile(obj, storage); +}; + +Profile.prototype.toObj = function() { + return JSON.parse(JSON.stringify(this)); +}; + + +Profile.prototype.store = function(cb) { +// TODO + return cb(); +// this.storage.setItem(this.hash, this.toObj()); +}; + + + +module.exports = Profile; + diff --git a/test/test.Profile.js b/test/test.Profile.js new file mode 100644 index 000000000..e7a066100 --- /dev/null +++ b/test/test.Profile.js @@ -0,0 +1,57 @@ +'use strict'; + +var chai = chai || require('chai'); +var should = chai.should(); +var bitcore = bitcore || require('bitcore'); +var buffertools = bitcore.buffertools; +var Profile = require('../js/models/Profile') +var sinon = require('sinon'); +var FakeStorage = function() {}; + +describe('Profile model', function() { + var email = 'email@pepe.com'; + var password = 'iamnotsatoshi'; + var storage = new FakeStorage(); + var opts = { + email: email, + password: password, + }; + + beforeEach(function() { + storage.getItem = sinon.stub(); + }); + + it('should fail create an instance', function() { + (function() { + new Profile({ + email: email, + }, storage) + }).should.throw('Illegal Arg'); + }); + + it('should create an instance', function() { + var p = new Profile({ + email: email, + password: password + }, storage); + should.exist(p); + }); + + it('#fromObj #toObj round trip', function() { + + var p = new Profile(opts, storage); + var p2 = Profile.fromObj(p.toObj(), storage); + p2.should.deep.equal(p); + }); + + it('#store', function(done) { + var p = new Profile(opts, storage); + p.store(function(err) { + should.not.exist(err); + done(); + }) + }); + + + +}); From 2bc19453094a43736b6f84cc52deb90c0b6bfd5d Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 16:43:10 -0300 Subject: [PATCH 004/182] refactor password param --- js/models/{Storage.js => WalletStorage.js} | 0 test/Storage.js | 6 ++++-- test/mocks/FakeLocalStorage.js | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) rename js/models/{Storage.js => WalletStorage.js} (100%) diff --git a/js/models/Storage.js b/js/models/WalletStorage.js similarity index 100% rename from js/models/Storage.js rename to js/models/WalletStorage.js diff --git a/test/Storage.js b/test/Storage.js index e48de991d..781bb591c 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -22,8 +22,10 @@ describe('Storage model', function() { it('should fail when encrypting without a password', function() { var s2 = new Storage(requireMock('FakeLocalStorage').storageParams); (function() { - s2._write(fakeWallet + timeStamp, 1, function() {}); - }).should.throw('NOPASSPHRASE'); + var params = _.clone(require('./mocks/FakeLocalStorage').storageParams); + params.password = undefined; + new Storage(params); + }).should.throw('Illegal Argument'); }); it('should be able to encrypt and decrypt', function(done) { s._write(fakeWallet + timeStamp, 'value', function() { diff --git a/test/mocks/FakeLocalStorage.js b/test/mocks/FakeLocalStorage.js index c2ae7ce14..47fdb8c1c 100644 --- a/test/mocks/FakeLocalStorage.js +++ b/test/mocks/FakeLocalStorage.js @@ -28,6 +28,7 @@ FakeLocalStorage.prototype.clear = function() { module.exports = FakeLocalStorage; module.exports.storageParams = { + password: '123', storage: new FakeLocalStorage(), sessionStorage: new FakeLocalStorage(), }; From a446d3775e3af989d7a934866b2825f691e08fca Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 16:49:53 -0300 Subject: [PATCH 005/182] refactor readWallet --- test/Storage.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Storage.js b/test/Storage.js index 781bb591c..17a63287f 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -343,8 +343,8 @@ describe('Storage model', function() { }); }); - describe('#readWallet', function() { - it('should read wallet', function(done) { + describe('#getFirst', function() { + it('should read first key', function(done) { var data = { 'wallet::id1_wallet1': { a: 'x', @@ -358,7 +358,7 @@ describe('Storage model', function() { sinon.stub(s, '_read', function(k, cb) { return cb(data[k]); }); - s.readWallet('id1', function(err, w) { + s.getFirst('wallet::id1', function(err, w) { should.not.exist(err); w.should.exist; w.hasOwnProperty('a').should.be.true; From c4c7dc8eb1b86a2000d60d37d5a0e5490a520edd Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 17:14:49 -0300 Subject: [PATCH 006/182] rm Identity#import --- js/controllers/import.js | 2 +- js/models/Identity.js | 17 +- js/models/WalletStorage.js | 358 ------------------------------------- test/Storage.js | 18 +- test/test.Identity.js | 15 +- 5 files changed, 18 insertions(+), 392 deletions(-) delete mode 100644 js/models/WalletStorage.js diff --git a/js/controllers/import.js b/js/controllers/import.js index 9b6de0a91..49c4d9c58 100644 --- a/js/controllers/import.js +++ b/js/controllers/import.js @@ -30,7 +30,7 @@ angular.module('copayApp.controllers').controller('ImportController', // try to import encrypted wallet with passphrase try { - w = identity.import(encryptedObj, passphrase, skipFields); + w = identity.fromEncryptedObj(encryptedObj, passphrase, skipFields); } catch (e) { errMsg = e.message; } diff --git a/js/models/Identity.js b/js/models/Identity.js index a65abbcaf..d496918ae 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -125,20 +125,6 @@ Identity.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) { return this.fromObj(walletObj, skipFields); }; -/** - * @TODO: import is a reserved keyword! DONT USE IT - * @TODO: this is essentialy the same method as {@link Identity#fromEncryptedObj}! - * @desc Imports a wallet from an encrypted base64 object - * @param {string} base64 - the base64 encoded object - * @param {string} passphrase - passphrase to decrypt it - * @param {string[]} skipFields - fields to ignore when importing - * @return {Wallet} - */ -Identity.prototype.import = function(base64, passphrase, skipFields) { - var self = this; - return self.fromEncryptedObj(base64, passphrase, skipFields); -}; - Identity.prototype.migrateWallet = function(walletId, passphrase, cb) { var self = this; @@ -173,7 +159,7 @@ Identity.prototype.read = function(walletId, skipFields, cb) { err; var obj = {}; - this.storage.readWallet(walletId, function(err, ret) { + this.storage.getFirst('wallet::' + walletId, function(err, ret) { if (err) return cb(err); _.each(Wallet.PERSISTED_PROPERTIES, function(p) { @@ -513,4 +499,5 @@ Identity.prototype.joinCreateSession = function(opts, cb) { }); }; + module.exports = Identity; diff --git a/js/models/WalletStorage.js b/js/models/WalletStorage.js deleted file mode 100644 index aa26ecbfb..000000000 --- a/js/models/WalletStorage.js +++ /dev/null @@ -1,358 +0,0 @@ -'use strict'; -var preconditions = require('preconditions').singleton(); -var CryptoJS = require('node-cryptojs-aes').CryptoJS; -var bitcore = require('bitcore'); -var preconditions = require('preconditions').instance(); -var _ = require('underscore'); -var CACHE_DURATION = 1000 * 60 * 5; -var id = 0; - -function Storage(opts) { - opts = opts || {}; - - this.wListCache = {}; - this.__uniqueid = ++id; - if (opts.password) - this.setPassphrase(opts.password); - - try { - this.storage = opts.storage || localStorage; - this.sessionStorage = opts.sessionStorage || sessionStorage; - } catch (e) { - console.log('Error in storage:', e); //TODO - }; - - preconditions.checkState(this.storage, 'No storage defined'); - preconditions.checkState(this.sessionStorage, 'No sessionStorage defined'); -} - -var pps = {}; -Storage.prototype._getPassphrase = function() { - - if (!pps[this.__uniqueid]) - throw new Error('NOPASSPHRASE: No passphrase set'); - - return pps[this.__uniqueid]; -} - -Storage.prototype.setPassphrase = function(password) { - pps[this.__uniqueid] = password; -} - -Storage.prototype._encrypt = function(string) { - var encrypted = CryptoJS.AES.encrypt(string, this._getPassphrase()); - var encryptedBase64 = encrypted.toString(); - return encryptedBase64; -}; - -Storage.prototype._decrypt = function(base64) { - var decryptedStr = null; - try { - var decrypted = CryptoJS.AES.decrypt(base64, this._getPassphrase()); - if (decrypted) - decryptedStr = decrypted.toString(CryptoJS.enc.Utf8); - } catch (e) { - // Error while decrypting - return null; - } - return decryptedStr; -}; - - -Storage.prototype._read = function(k, cb) { - preconditions.checkArgument(cb); - - var self = this; - this.storage.getItem(k, function(ret) { - if (!ret) return cb(null); - var ret = self._decrypt(ret); - if (!ret) return cb(null); - - ret = ret.toString(CryptoJS.enc.Utf8); - ret = JSON.parse(ret); - return cb(ret); - }); -}; - -Storage.prototype._write = function(k, v, cb) { - preconditions.checkArgument(cb); - - v = JSON.stringify(v); - v = this._encrypt(v); - this.storage.setItem(k, v, cb); -}; - -// get value by key -Storage.prototype.getGlobal = function(k, cb) { - preconditions.checkArgument(cb); - - this.storage.getItem(k, function(item) { - cb(item == 'undefined' ? undefined : item); - }); -}; - -// set value for key -Storage.prototype.setGlobal = function(k, v, cb) { - preconditions.checkArgument(cb); - this.storage.setItem(k, typeof v === 'object' ? JSON.stringify(v) : v, cb); -}; - -// remove value for key -Storage.prototype.removeGlobal = function(k, cb) { - preconditions.checkArgument(cb); - this.storage.removeItem(k, cb); -}; - -Storage.prototype.getSessionId = function(cb) { - preconditions.checkArgument(cb); - var self = this; - - self.sessionStorage.getItem('sessionId', function(sessionId) { - if (sessionId) - return cb(sessionId); - - sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex'); - self.sessionStorage.setItem('sessionId', sessionId, function() { - return cb(sessionId); - }); - }); -}; - -Storage.prototype.setSessionId = function(sessionId, cb) { - this.sessionStorage.setItem('sessionId', sessionId, cb); -}; - -Storage.prototype._readHelper = function(walletId, k, cb) { - var wk = this._key(walletId, k); - this._read(wk, function(v) { - return cb(v, k); - }); -}; - -Storage.prototype.readWallet_Old = function(walletId, cb) { - var self = this; - this.storage.allKeys(function(allKeys) { - var obj = {}; - var keys = _.filter(allKeys, function(k) { - if (k.indexOf(walletId + '::') === 0) return true; - }); - if (keys.length === 0) return cb(new Error('Wallet ' + walletId + ' not found')); - var count = keys.length; - _.each(keys, function(k) { - self._read(k, function(v) { - obj[k.split('::')[1]] = v; - if (--count === 0) return cb(null, obj); - }) - }); - }); -}; - -Storage.prototype.readWallet = function(walletId, cb) { - var self = this; - this.storage.allKeys(function(allKeys) { - var keys = _.filter(allKeys, function(k) { - if ((k === 'wallet::' + walletId) || k.indexOf('wallet::' + walletId) === 0) return true; - }); - if (keys.length === 0) return cb(new Error('Wallet ' + walletId + ' not found')); - self._read(keys[0], function(v) { - if (_.isNull(v)) return cb(new Error('Could not decrypt wallet data')); - return cb(null, v); - }) - }); -}; - -Storage.prototype.getMany = function(walletId, keys, cb) { - preconditions.checkArgument(cb); - - var self = this; - var ret = {}; - - var l = keys.length, - i = 0; - - for (var ii in keys) { - this._readHelper(walletId, keys[ii], function(v, k) { - ret[k] = v; - if (++i == l) { - return cb(ret); - } - }); - } -}; - -Storage.prototype._getWalletIds = function(cb) { - preconditions.checkArgument(cb); - var walletIds = []; - var uniq = {}; - this.storage.allKeys(function(keys) { - for (var ii in keys) { - var key = keys[ii]; - var split = key.split('::'); - if (split.length == 2) { - var walletId = split[0]; - - if (!walletId || walletId === 'nameFor' || walletId === 'lock' || walletId === 'wallet') - continue; - - if (typeof uniq[walletId] === 'undefined') { - walletIds.push(walletId); - uniq[walletId] = 1; - } - } - } - return cb(walletIds); - }); -}; - -Storage.prototype.getWallets_Old = function(cb) { - preconditions.checkArgument(cb); - - if (this.wListCache.ts > Date.now()) - return cb(this.wListCache.data) - - var wallets = []; - var self = this; - - this._getWalletIds(function(ids) { - var l = ids.length, - i = 0; - if (!l) - return cb([]); - - _.each(ids, function(id) { - self.getGlobal('nameFor::' + id, function(name) { - wallets.push({ - id: id, - name: name, - }); - if (++i == l) { - self.wListCache.data = wallets; - self.wListCache.ts = Date.now() + CACHE_DURATION; - return cb(wallets); - } - }); - }); - }); -}; - -Storage.prototype.getWallets2 = function(cb) { - var self = this; - var re = /wallet::([^_]+)(_?(.*))/; - - this.storage.allKeys(function(allKeys) { - var wallets = _.compact(_.map(allKeys, function(key) { - if (key.indexOf('wallet::') !== 0) - return null; - var match = key.match(re); - if (match.length != 4) - return null; - return { - id: match[1], - name: match[3] ? match[3] : undefined, - }; - })); - - return cb(wallets); - }); -}; - -Storage.prototype.getWallets = function(cb) { - var self = this; - self.getWallets2(function(wallets) { - self.getWallets_Old(function(wallets2) { - var ids = _.pluck(wallets, 'id'); - _.each(wallets2, function(w) { - if (!_.contains(ids, w.id)) - wallets.push(w); - }); - return cb(wallets); - }); - }) -}; - - -Storage.prototype.deleteWallet_Old = function(walletId, cb) { - preconditions.checkArgument(walletId); - preconditions.checkArgument(cb); - var err; - var self = this; - - var toDelete = {}; - - this.storage.allKeys(function(allKeys) { - for (var ii in allKeys) { - var key = allKeys[ii]; - var split = key.split('::'); - if (split.length == 2 && split[0] === walletId) { - toDelete[key] = 1; - }; - } - var l = Object.keys(toDelete).length, - j = 0; - if (!l) - return cb(new Error('WNOTFOUND: Wallet not found')); - - toDelete['nameFor::' + walletId] = 1; - l++; - - for (var i in toDelete) { - self.removeGlobal(i, function() { - if (++j == l) - return cb(err); - }); - - } - }); -}; - -Storage.prototype.deleteWallet = function(walletId, cb) { - preconditions.checkArgument(walletId); - preconditions.checkArgument(cb); - - var self = this; - this.getWallets2(function(wallets) { - var w = _.findWhere(wallets, { - id: walletId - }); - if (!w) - return cb(new Error('WNOTFOUND: Wallet not found')); - self.removeGlobal('wallet::' + walletId + (w.name ? '_' + w.name : ''), function() { - return cb(); - }); - }); -}; - -Storage.prototype.setLastOpened = function(walletId, cb) { - this.setGlobal('lastOpened', walletId, cb); -}; - -Storage.prototype.getLastOpened = function(cb) { - this.getGlobal('lastOpened', cb); -}; - -Storage.prototype.setFromObj = function(key, obj, cb) { - preconditions.checkArgument(key); - preconditions.checkArgument(cb); - this._write(key, obj, function() { - return cb(); - }); -}; - - -// remove all values -Storage.prototype.clearAll = function(cb) { - this.storage.clear(cb); -}; - -Storage.prototype.import = function(base64) { - var decryptedStr = this._decrypt(base64); - return JSON.parse(decryptedStr); -}; - -Storage.prototype.export = function(obj) { - var string = JSON.stringify(obj); - return this._encrypt(string); -}; - - -module.exports = Storage; diff --git a/test/Storage.js b/test/Storage.js index 17a63287f..dafda888d 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -81,11 +81,11 @@ describe('Storage model', function() { }); }); - describe('#_getWalletIds', function() { + describe('#_getWalletIds_Old', function() { it('should get wallet ids', function(done) { s._write('1::hola', 'juan', function() { s._write('2::hola', 'juan', function() { - s._getWalletIds(function(v) { + s._getWalletIds_Old(function(v) { v.should.deep.equal(['1', '2']); done(); }); @@ -118,13 +118,13 @@ describe('Storage model', function() { }); } - describe('#getWallets_Old', function() { + describe('#getWallets1_Old', function() { it('should retrieve wallets from storage', function(done) { s._write('1::hola', 'juan', function() { s._write('2::hola', 'juan', function() { s.setGlobal('nameFor::1', 'hola', function() { - s.getWallets_Old(function(ws) { + s.getWallets1_Old(function(ws) { ws[0].should.deep.equal({ id: '1', name: 'hola', @@ -151,7 +151,7 @@ describe('Storage model', function() { }, 1); }; - s.getWallets_Old(function(ws) { + s.getWallets1_Old(function(ws) { ws[0].should.deep.equal({ id: '1', name: 'hola', @@ -168,7 +168,7 @@ describe('Storage model', function() { }); }); - describe('#getWallets2', function() { + describe('#getWallets2_Old', function() { it('should retrieve wallets from storage', function(done) { var w1 = { name: 'juan', @@ -181,7 +181,7 @@ describe('Storage model', function() { }; s.setFromObj('wallet::1_wallet1', w1, function() { s.setFromObj('wallet::2', w2, function() { - s.getWallets2(function(ws) { + s.getWallets2_Old(function(ws) { ws[0].should.deep.equal({ id: '1', name: 'wallet1', @@ -215,7 +215,7 @@ describe('Storage model', function() { s._write('3::name', 'matias', function() { s._write('1::name', 'juan', function() { s.setGlobal('nameFor::3', 'wallet3', function() { - s.getWallets(function(ws) { + s.getWallets_Old(function(ws) { ws.length.should.equal(3); ws[0].should.deep.equal({ id: '1', @@ -307,7 +307,7 @@ describe('Storage model', function() { s.setFromObj('wallet::2', w2, function() { s.deleteWallet('1', function(err) { should.not.exist(err); - s.getWallets2(function(ws) { + s.getWallets2_Old(function(ws) { ws.length.should.equal(1); ws[0].id.should.equal('2'); done(); diff --git a/test/test.Identity.js b/test/test.Identity.js index 914a8e0c8..dcb3e5272 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -162,9 +162,6 @@ describe('Identity model', function() { wf.storage.import.calledOnce.should.be.true; wf.fromObj.calledWith('walletObj').should.be.true; }); - }); - - describe('#import', function() { it('should import and update indexes', function() { var wallet = { id: "fake wallet", @@ -174,14 +171,14 @@ describe('Identity model', function() { }; wf.fromEncryptedObj = sinon.stub().returns(wallet); - var w = wf.import("encrypted", "password"); + var w = wf.fromEncryptedObj("encrypted", "password"); should.exist(w); wallet.should.equal(w); }); it('should import with a wrong password', function() { wf.fromEncryptedObj = sinon.stub().returns(null); - var w = wf.import("encrypted", "passwordasdfasdf"); + var w = wf.fromEncryptedObj("encrypted", "passwordasdfasdf"); should.not.exist(w); }); }); @@ -272,7 +269,7 @@ describe('Identity model', function() { describe('#read', function() { it('should fail to read unexisting wallet', function(done) { - wf.storage.readWallet = sinon.stub().yields(null, {}); + wf.storage.getFirst = sinon.stub().yields(null, {}); wf.read('id', [], function(err, w) { should.not.exist(w); @@ -284,7 +281,7 @@ describe('Identity model', function() { }); }); it('should fail to read broken wallet', function(done) { - wf.storage.readWallet = sinon.stub().yields(null, { + wf.storage.getFirst = sinon.stub().yields(null, { 'opts': 1 }); wf.read('id', [], function(err, w) { @@ -298,7 +295,7 @@ describe('Identity model', function() { }); it('should read existing wallet', function(done) { var wf = new Identity(config, '0.0.1'); - wf.storage.readWallet = sinon.stub().yields(null, { + wf.storage.getFirst = sinon.stub().yields(null, { 'opts': 1 }); wf.fromObj = sinon.stub().returns('ok'); @@ -568,7 +565,7 @@ describe('Identity model', function() { wf.storage.setPassphrase = sinon.spy(); wf.storage.import.withArgs('dummy').returns(JSON.parse(legacy1)); - var w = wf.import('dummy', 'xxx'); + var w = wf.fromEncryptedObj('dummy', 'xxx'); should.exist(w); wf.storage.setPassphrase.calledOnce.should.equal(true); wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); From 028a300012a21b5d47b561b9b4f01e381a63423e Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 18:00:27 -0300 Subject: [PATCH 007/182] rm storage.storage to storage.db --- js/models/Identity.js | 3 ++- js/models/PluginManager.js | 2 +- js/models/Profile.js | 31 ++++++++++++++++++------------- test/Storage.js | 11 +++++------ test/WalletLock.js | 7 +++---- test/mocks/FakeLocalStorage.js | 5 +++-- test/test.Profile.js | 14 +++++++------- 7 files changed, 39 insertions(+), 34 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index d496918ae..b7c389517 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -8,6 +8,7 @@ var Wallet = require('./Wallet'); var _ = require('underscore'); var log = require('../log'); var PluginManager = require('./PluginManager'); +var Profile = require('./Profile'); var Async = module.exports.Async = require('./Async'); var Insight = module.exports.Insight = require('./Insight'); var preconditions = require('preconditions').singleton(); @@ -48,7 +49,7 @@ function Identity(config, version, pluginManager) { if (pluginManager) { storageOpts = { - storage: pluginManager.get('STORAGE') + storage: pluginManager.get('DB') }; } diff --git a/js/models/PluginManager.js b/js/models/PluginManager.js index 57eaea6b9..6a58b6eb6 100644 --- a/js/models/PluginManager.js +++ b/js/models/PluginManager.js @@ -24,7 +24,7 @@ var KIND_UNIQUE = PluginManager.KIND_UNIQUE = 1; var KIND_MULTIPLE = PluginManager.KIND_MULTIPLE = 2; PluginManager.TYPE = {}; -PluginManager.TYPE['STORAGE'] = KIND_UNIQUE; +PluginManager.TYPE['DB'] = KIND_UNIQUE; PluginManager.prototype._register = function(obj, name) { preconditions.checkArgument(obj.type, 'Plugin has not type:' + name); diff --git a/js/models/Profile.js b/js/models/Profile.js index 9665710c7..ecb6926f8 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -4,35 +4,40 @@ var _ = require('underscore'); var log = require('../log'); var bitcore = require('bitcore'); -function Profile(opts, storage) { +function Profile(opts, password, storage) { preconditions.checkArgument(opts.email); - preconditions.checkArgument(opts.password); + preconditions.checkArgument(password); preconditions.checkArgument(storage); preconditions.checkArgument(storage.getItem); this.email = opts.email; - this.password = opts.password; - this.hash = bitcore.util.sha256ripe160(this.email + this.password); - + this.hash = bitcore.util.sha256ripe160(this.email + this.password).toString('hex'); + this.storage = storage; this.extra = opts.extra; }; -Profile.fromObj = function(obj, storage) { - return new Profile(obj, storage); +Profile.fromObj = function(obj, password, storage) { + var o = _.clone(obj); + return new Profile(obj, password, storage); }; Profile.prototype.toObj = function() { - return JSON.parse(JSON.stringify(this)); + var obj = _.clone(this); + delete obj['hash']; + return JSON.parse(JSON.stringify(obj)); }; Profile.prototype.store = function(cb) { -// TODO - return cb(); -// this.storage.setItem(this.hash, this.toObj()); + var val = this.toObj(); + var key = 'identity::' + this.hash + '_' + this.email; + + this.storage.setFromObj(key, val, function(err) { + log.debug('Identity stored'); + if (cb) + cb(err); + }); }; - - module.exports = Profile; diff --git a/test/Storage.js b/test/Storage.js index dafda888d..cd5c87104 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -7,11 +7,10 @@ var timeStamp = Date.now(); describe('Storage model', function() { var s; - beforeEach(function() { + beforeEach(function(done) { s = new Storage(requireMock('FakeLocalStorage').storageParams); s.setPassphrase('mysupercoolpassword'); - s.storage.clear(); - s.sessionStorage.clear(); + s.clearAll(done); }); @@ -22,7 +21,7 @@ describe('Storage model', function() { it('should fail when encrypting without a password', function() { var s2 = new Storage(requireMock('FakeLocalStorage').storageParams); (function() { - var params = _.clone(require('./mocks/FakeLocalStorage').storageParams); + var params = _.clone(requireMock('FakeLocalStorage').storageParams); params.password = undefined; new Storage(params); }).should.throw('Illegal Argument'); @@ -325,7 +324,7 @@ describe('Storage model', function() { 'id1::b': 'y', 'id2::c': 'z', }; - s.storage.allKeys = sinon.stub().yields(_.keys(data)); + s.db.allKeys = sinon.stub().yields(_.keys(data)); sinon.stub(s, '_read', function(k, cb) { return cb(data[k]); }); @@ -354,7 +353,7 @@ describe('Storage model', function() { c: 'z' }, }; - s.storage.allKeys = sinon.stub().yields(_.keys(data)); + s.db.allKeys = sinon.stub().yields(_.keys(data)); sinon.stub(s, '_read', function(k, cb) { return cb(data[k]); }); diff --git a/test/WalletLock.js b/test/WalletLock.js index 6f680c395..14b86409d 100644 --- a/test/WalletLock.js +++ b/test/WalletLock.js @@ -11,8 +11,7 @@ describe('WalletLock model', function() { beforeEach(function() { storage = new Storage(requireMock('FakeLocalStorage').storageParams); storage.setPassphrase('mysupercoolpassword'); - storage.storage.clear(); - storage.sessionStorage.clear(); + storage.clearAll(); }); it('should fail with missing args', function() { @@ -85,9 +84,9 @@ describe('WalletLock model', function() { w.keepAlive(function() { storage.setSessionId('session2', function() { - var json = JSON.parse(storage.storage.ls['lock::walletId']); + var json = JSON.parse(storage.db.ls['lock::walletId']); json.expireTs -= 3600 * 1000; - storage.storage.ls['lock::walletId'] = JSON.stringify(json); + storage.db.ls['lock::walletId'] = JSON.stringify(json); var w2 = new WalletLock(storage, 'walletId'); w2.keepAlive(function(locked) { w2.sessionId.should.equal('session2'); diff --git a/test/mocks/FakeLocalStorage.js b/test/mocks/FakeLocalStorage.js index 47fdb8c1c..56a6b5aab 100644 --- a/test/mocks/FakeLocalStorage.js +++ b/test/mocks/FakeLocalStorage.js @@ -21,14 +21,15 @@ FakeLocalStorage.prototype.setItem = function(k, v, cb) { this.ls[k] = v; return cb(); }; -FakeLocalStorage.prototype.clear = function() { +FakeLocalStorage.prototype.clear = function(cb) { this.ls = {}; + if (cb) return cb(); } module.exports = FakeLocalStorage; module.exports.storageParams = { password: '123', - storage: new FakeLocalStorage(), + db: new FakeLocalStorage(), sessionStorage: new FakeLocalStorage(), }; diff --git a/test/test.Profile.js b/test/test.Profile.js index e7a066100..7d16d09fb 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -14,11 +14,12 @@ describe('Profile model', function() { var storage = new FakeStorage(); var opts = { email: email, - password: password, }; beforeEach(function() { storage.getItem = sinon.stub(); + storage.setFromObj = sinon.stub(); + storage.setFromObj.yields(null); }); it('should fail create an instance', function() { @@ -32,21 +33,20 @@ describe('Profile model', function() { it('should create an instance', function() { var p = new Profile({ email: email, - password: password - }, storage); + }, password, storage); should.exist(p); }); it('#fromObj #toObj round trip', function() { - - var p = new Profile(opts, storage); - var p2 = Profile.fromObj(p.toObj(), storage); + var p = new Profile(opts, password, storage); + var p2 = Profile.fromObj(p.toObj(), password, storage); p2.should.deep.equal(p); }); it('#store', function(done) { - var p = new Profile(opts, storage); + var p = new Profile(opts, password, storage); p.store(function(err) { + storage.setFromObj.getCall(0).args[1].should.deep.equal(p.toObj()); should.not.exist(err); done(); }) From d7b7633fefb9f36017fe0a1cea0633ea1dade030 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 18:53:34 -0300 Subject: [PATCH 008/182] skip compat tests in Identity --- js/models/Identity.js | 42 ++++++++++++++++++------------------------ test/test.Identity.js | 23 ++++++++++++++++------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index b7c389517..f7f23c25e 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -9,7 +9,6 @@ var _ = require('underscore'); var log = require('../log'); var PluginManager = require('./PluginManager'); var Profile = require('./Profile'); -var Async = module.exports.Async = require('./Async'); var Insight = module.exports.Insight = require('./Insight'); var preconditions = require('preconditions').singleton(); var Storage = module.exports.Storage = require('./Storage'); @@ -19,17 +18,6 @@ var Storage = module.exports.Storage = require('./Storage'); * Identity - stores the state for a wallet in creation * * @param {Object} config - configuration for this wallet - * - * @TODO: Don't pass a class for these three components - * -- send a factory or instance, the 'new' call considered harmful for refactoring - * -- arguable, since all of them is called with an object as argument. - * -- Still, could it be hard to refactor? (for example, what if we want to fail hard if a network call gets interrupted?) - * @param {Storage} config.Storage - the class to instantiate to store the wallet (StorageLocalEncrypted by default) - * @param {Object} config.storage - the configuration to be sent to the Storage constructor - * @param {Network} config.Network - the class to instantiate to make network requests to copayers (the Async module by default) - * @param {Object} config.network - the configurations to be sent to the Network and Blockchain constructors - * @param {Blockchain} config.Blockchain - the class to instantiate to get information about the blockchain (Insight by default) - * @TODO: Investigate what parameters go inside this object * @param {Object} config.wallet - default configuration for the wallet * @TODO: put `version` inside of the config object * @param {string} version - the version of copay for which this wallet was generated (for example, 0.4.7) @@ -39,29 +27,30 @@ var Storage = module.exports.Storage = require('./Storage'); function Identity(config, version, pluginManager) { var self = this; preconditions.checkArgument(config); - preconditions.checkArgument(config.network); - - this.Storage = config.Storage || Storage; - this.Network = config.Network || Async; - this.Blockchain = config.Blockchain || Insight; - var storageOpts = {}; if (pluginManager) { storageOpts = { - storage: pluginManager.get('DB') + db: pluginManager.get('DB') }; + /* + * TODO (plugins for other services) + * + * blockchainOpts = { + * provider: Insight... + * } + */ } - this.storage = new this.Storage(storageOpts); + this.storage = this._getStorage(storageOpts); this.networks = { - 'livenet': new this.Network(config.network.livenet), - 'testnet': new this.Network(config.network.testnet), + 'livenet': new Insight(config.network.livenet), + 'testnet': new Insight(config.network.testnet), }; this.blockchains = { - 'livenet': new this.Blockchain(config.network.livenet), - 'testnet': new this.Blockchain(config.network.testnet), + 'livenet': new Insight(config.network.livenet), + 'testnet': new Insight(config.network.testnet), }; this.walletDefaults = config.wallet || {}; @@ -69,6 +58,11 @@ function Identity(config, version, pluginManager) { }; +/* for stubbing */ +Identity.prototype._getStorage = function(opts) { + return new Storage(opts); +}; + /** * @desc obtain network name from serialized wallet * @param {Object} wallet object diff --git a/test/test.Identity.js b/test/test.Identity.js index dcb3e5272..c4ec10fc4 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -32,12 +32,16 @@ describe('Identity model', function() { var wf; beforeEach(function() { - wf = new Identity(config, '0.0.1'); + var s = sinon.stub(); - wf.storage.setPassphrase = sinon.spy(); - wf.storage.getSessionId = sinon.spy(); - wf.storage.setFromObj = sinon.spy(); - wf.storage.setLastOpened = sinon.stub().yields(null); + Identity.prototype._getStorage = sinon.stub().returns(s); + + wf = new Identity(config,'0.0.1'); + + s.setPassphrase = sinon.spy(); + s.getSessionId = sinon.spy(); + s.setFromObj = sinon.spy(); + s.setLastOpened = sinon.stub().yields(null); var w = sinon.stub(); @@ -97,7 +101,7 @@ describe('Identity model', function() { }); // TODO this is a WALLET TEST! not Wallet Factory. Move it. - describe('#fromObj / #toObj', function() { + describe.skip('#fromObj / #toObj', function() { it('round trip', function() { var wf = new Identity(config, '0.0.5'); var original = JSON.parse(o); @@ -443,6 +447,8 @@ describe('Identity model', function() { it('should yield bad network error', function(done) { var net = wf.networks['testnet']; net.greet = sinon.stub(); + net.cleanUp = sinon.stub(); + net.start = sinon.stub().yields(null); net.on = sinon.stub(); net.on.withArgs('data').yields('senderId', { type: 'walletId', @@ -461,6 +467,9 @@ describe('Identity model', function() { opts.privHex = undefined; var net = wf.networks['testnet']; net.greet = sinon.stub(); + net.cleanUp = sinon.stub(); + net.start = sinon.stub().yields(null); + net.on = sinon.stub(); net.on.withArgs('serverError').yields(null); net.on.withArgs('data').yields('senderId', { @@ -547,7 +556,7 @@ describe('Identity model', function() { }); - describe('Backwards compatibility tests', function() { + describe.skip('Backwards compatibility tests', function() { it('should be able to import unencrypted legacy wallet TxProposal: v0', function() { var wf = new Identity(config, '0.0.5'); var w = wf.fromObj(JSON.parse(legacyO)); From 591c8c84b6de333813ce3c600384418befac20c4 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 27 Sep 2014 18:56:25 -0300 Subject: [PATCH 009/182] skip compat tests in Identity --- js/models/Identity.js | 4 ++-- test/test.Identity.js | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index f7f23c25e..0b6e0eb85 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -243,7 +243,7 @@ Identity.prototype._getWallet = function(opts) { * @param {callback} opts.version * @return {Wallet} */ -Identity.prototype.create = function(opts, cb) { +Identity.prototype.createWallet = function(opts, cb) { preconditions.checkArgument(cb); opts = opts || {}; @@ -415,7 +415,7 @@ Identity.prototype.decodeSecret = function(secret) { * @param {string} opts.privateHex - the private extended master key * @param {walletCreationCallback} cb - a callback */ -Identity.prototype.joinCreateSession = function(opts, cb) { +Identity.prototype.joinWallet = function(opts, cb) { preconditions.checkArgument(opts); preconditions.checkArgument(opts.secret); preconditions.checkArgument(opts.passphrase); diff --git a/test/test.Identity.js b/test/test.Identity.js index c4ec10fc4..d68930087 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -405,9 +405,9 @@ describe('Identity model', function() { }); }); - describe('#create', function() { + describe('#createWallet', function() { it('should create wallet', function(done) { - wf.create(null, function(err, w) { + wf.createWallet(null, function(err, w) { should.exist(w); should.not.exist(err); done(); @@ -416,7 +416,7 @@ describe('Identity model', function() { it('should be able to create wallets with given pk', function(done) { var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; - wf.create({ + wf.createWallet({ privateKeyHex: priv, }, function(err, w) { wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); @@ -426,8 +426,8 @@ describe('Identity model', function() { }); it('should be able to create wallets with random pk', function(done) { - wf.create(null, function(err, w1) { - wf.create(null, function(err, w2) { + wf.createWallet(null, function(err, w1) { + wf.createWallet(null, function(err, w2) { wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( wf._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString ); @@ -437,7 +437,7 @@ describe('Identity model', function() { }); }); - describe('#joinCreateSession', function() { + describe('#joinWallet', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', nickname: 'test', @@ -456,7 +456,7 @@ describe('Identity model', function() { opts: {}, }); opts.privHex = undefined; - wf.joinCreateSession(opts, function(err, w) { + wf.joinWallet(opts, function(err, w) { err.should.equal('badNetwork'); done(); }); @@ -476,7 +476,7 @@ describe('Identity model', function() { type: 'walletId', networkName: wf.networkName, }); - wf.joinCreateSession(opts, function(err, w) { + wf.joinWallet(opts, function(err, w) { err.should.equal('joinError'); done(); }); @@ -500,11 +500,11 @@ describe('Identity model', function() { var w = sinon.stub(); w.sendWalletReady = sinon.spy(); - wf.create = sinon.stub().yields(null, w); - wf.joinCreateSession(opts, function(err, w) { + wf.createWallet = sinon.stub().yields(null, w); + wf.joinWallet(opts, function(err, w) { net.start.calledOnce.should.equal(true); - wf.create.calledOnce.should.equal(true); - wf.create.calledOnce.should.equal(true); + wf.createWallet.calledOnce.should.equal(true); + wf.createWallet.calledOnce.should.equal(true); w.sendWalletReady.calledOnce.should.equal(true); w.sendWalletReady.getCall(0).args[0].should.equal('03ddbc4711534bc62ccf576ab05f2a0afd11f9e2f4016781f3f5a88de9543a229a'); @@ -526,8 +526,8 @@ describe('Identity model', function() { networkName: 'testnet', opts: {}, }); - wf.create = sinon.stub().yields(null, null); - wf.joinCreateSession(opts, function(err, w) { + wf.createWallet = sinon.stub().yields(null, null); + wf.joinWallet(opts, function(err, w) { err.should.equal('walletFull'); done(); }); @@ -539,7 +539,7 @@ describe('Identity model', function() { var net = wf.networks['testnet']; net.cleanUp = sinon.spy(); net.start = sinon.spy(); - wf.joinCreateSession(opts, function(err, w) { + wf.joinWallet(opts, function(err, w) { net.start.getCall(0).args[0].privkey.should.equal('ddc2fa8c583a73c4b2a24630ec7c283df4e7c230a02c4e48bc36ec61687afd7d'); }); }); @@ -549,7 +549,7 @@ describe('Identity model', function() { var net = wf.networks['testnet']; net.cleanUp = sinon.spy(); net.start = sinon.spy(); - wf.joinCreateSession(opts, function(err, w) { + wf.joinWallet(opts, function(err, w) { net.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer }); }); From 0afc5f2c0c21905cc009343baa408513fd4c920f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sun, 28 Sep 2014 18:38:06 -0300 Subject: [PATCH 010/182] test for new Iden functions --- js/models/Identity.js | 135 ++++++++++++++---- js/models/Profile.js | 8 +- test/test.Identity.js | 324 +++++++++++++++++++++++------------------- 3 files changed, 293 insertions(+), 174 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 0b6e0eb85..84cb41595 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -7,6 +7,7 @@ var PrivateKey = require('./PrivateKey'); var Wallet = require('./Wallet'); var _ = require('underscore'); var log = require('../log'); +var version = require('../../version').version; var PluginManager = require('./PluginManager'); var Profile = require('./Profile'); var Insight = module.exports.Insight = require('./Insight'); @@ -19,20 +20,17 @@ var Storage = module.exports.Storage = require('./Storage'); * * @param {Object} config - configuration for this wallet * @param {Object} config.wallet - default configuration for the wallet - * @TODO: put `version` inside of the config object - * @param {string} version - the version of copay for which this wallet was generated (for example, 0.4.7) * @constructor */ -function Identity(config, version, pluginManager) { - var self = this; - preconditions.checkArgument(config); +function Identity(email, password, opts) { + preconditions.checkArgument(opts); var storageOpts = {}; - if (pluginManager) { - storageOpts = { - db: pluginManager.get('DB') - }; + if (opts.pluginManager) { + storageOpts = _.clone({ + db: opts.pluginManager.get('DB') + }); /* * TODO (plugins for other services) * @@ -41,28 +39,115 @@ function Identity(config, version, pluginManager) { * } */ } + storageOpts.password = password; - this.storage = this._getStorage(storageOpts); + this.storage = Identity._newStorage(storageOpts); this.networks = { - 'livenet': new Insight(config.network.livenet), - 'testnet': new Insight(config.network.testnet), + 'livenet': Identity._newInsight(opts.network.livenet), + 'testnet': Identity._newInsight(opts.network.testnet), }; this.blockchains = { - 'livenet': new Insight(config.network.livenet), - 'testnet': new Insight(config.network.testnet), + 'livenet': Identity._newInsight(opts.network.livenet), + 'testnet': Identity._newInsight(opts.network.testnet), }; - this.walletDefaults = config.wallet || {}; - this.version = version; + this.walletDefaults = opts.wallet || {}; + this.version = opts.version || version; + + this.wallets = []; + this.profile = Identity._newProfile({ + email: email, + }, password, this.storage); }; /* for stubbing */ -Identity.prototype._getStorage = function(opts) { +Identity._newProfile = function(info, password, storage) { + return new Profile(info, password, storage); +}; + +/* for stubbing */ +Identity._newInsight = function(opts) { + return new Insight(opts); +}; + + +/* for stubbing */ +Identity._newStorage = function(opts) { return new Storage(opts); }; + +/** + * creates and Identity + * + * @param email + * @param password + * @param opts + * @param cb + * @return {undefined} + */ +Identity.create = function(email, password, opts, cb) { + var iden = new Identity(email, password, opts); + iden.store({ + overwrite: false, + }, function(err) { + return cb(err, iden); + }); +}; + + +/** + * validates Profile's email + * + * @param authcode + * @param cb + * @return {undefined} + */ +Identity.prototype.validate = function(authcode, cb) { + // TODO + console.log('[Identity.js.99] TODO: Should validate email thru authcode'); //TODO + return cb(); +}; + + +/** + * open's an Identity from storage + * + * @param email + * @param password + * @param opts + * @param cb + * @return {undefined} + */ +Identity.open = function(email, password, opts, cb) { + var iden = new Identity(email, password, opts); + iden.read(function(err){ + return cb(err, iden); + }); +}; + +/** + * isAvailable + * + * @param email + * @param opts + * @param cb + * @return {undefined} + */ +Identity.isAvailable = function(email, opts, cb) { + console.log('[Identity.js.127:isAvailable:] TODO'); //TODO + return cb(); +}; + + +Identity.prototype.store = function(opts, cb) { + console.log('[Identity.js.142] TODO .store'); //TODO + return cb(); +}; + + /** * @desc obtain network name from serialized wallet * @param {Object} wallet object @@ -81,7 +166,7 @@ Identity.prototype.obtainNetworkName = function(obj) { * @param {string[]} skipFields - fields to skip when importing * @return {Wallet} */ -Identity.prototype.fromObj = function(inObj, skipFields) { +Identity.prototype._fromObj = function(inObj, skipFields) { var networkName = this.obtainNetworkName(inObj); preconditions.checkState(networkName); preconditions.checkArgument(inObj); @@ -113,7 +198,7 @@ Identity.prototype.fromObj = function(inObj, skipFields) { * @param {string[]} skipFields - fields to ignore when importing * @return {Wallet} */ -Identity.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) { +Identity.prototype.importWallet = function(base64, passphrase, skipFields) { this.storage.setPassphrase(passphrase); var walletObj = this.storage.import(base64); if (!walletObj) return false; @@ -149,7 +234,7 @@ Identity.prototype.migrateWallet = function(walletId, passphrase, cb) { * @param {string[]} skipFields - parameters to ignore when importing * @param {function} callback - {err, Wallet} */ -Identity.prototype.read = function(walletId, skipFields, cb) { +Identity.prototype._readWallet = function(walletId, skipFields, cb) { var self = this, err; var obj = {}; @@ -330,13 +415,13 @@ Identity.prototype._checkVersion = function(inVersion) { * @param {function} callback (err, {Wallet}) * @return */ -Identity.prototype.open = function(walletId, passphrase, cb) { +Identity.prototype.openWallet = function(walletId, passphrase, cb) { preconditions.checkArgument(cb); var self = this; self.storage.setPassphrase(passphrase); self.migrateWallet(walletId, passphrase, function() { - self.read(walletId, null, function(err, w) { + self._readWallet(walletId, null, function(err, w) { if (err) return cb(err); w.store(function(err) { @@ -348,7 +433,7 @@ Identity.prototype.open = function(walletId, passphrase, cb) { }); }; -Identity.prototype.getWallets = function(cb) { +Identity.prototype.listWallets = function(cb) { var self = this; this.storage.getWallets(function(wallets) { wallets.forEach(function(i) { @@ -373,7 +458,7 @@ Identity.prototype.getWallets = function(cb) { * @callback cb * @return {?} the result of the callback */ -Identity.prototype.delete = function(walletId, cb) { +Identity.prototype.deleteWallet = function(walletId, cb) { var self = this; self.storage.deleteWallet(walletId, function(err) { if (err) return cb(err); @@ -479,7 +564,7 @@ Identity.prototype.joinWallet = function(opts, cb) { walletOpts.nickname = opts.nickname; walletOpts.passphrase = opts.passphrase; - self.create(walletOpts, function(err, w) { + self.createWallet(walletOpts, function(err, w) { if (w) { w.sendWalletReady(decodedSecret.pubKey); diff --git a/js/models/Profile.js b/js/models/Profile.js index ecb6926f8..61e01425a 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -4,16 +4,16 @@ var _ = require('underscore'); var log = require('../log'); var bitcore = require('bitcore'); -function Profile(opts, password, storage) { - preconditions.checkArgument(opts.email); +function Profile(info, password, storage) { + preconditions.checkArgument(info.email); preconditions.checkArgument(password); preconditions.checkArgument(storage); preconditions.checkArgument(storage.getItem); - this.email = opts.email; + this.email = info.email; + this.extra = info.extra; this.hash = bitcore.util.sha256ripe160(this.email + this.password).toString('hex'); this.storage = storage; - this.extra = opts.extra; }; Profile.fromObj = function(obj, password, storage) { diff --git a/test/test.Identity.js b/test/test.Identity.js index d68930087..786fa179f 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -28,39 +28,39 @@ function assertObjectEqual(a, b) { describe('Identity model', function() { - - var wf; + var iden, storage, wallet, profile; beforeEach(function() { - var s = sinon.stub(); - - Identity.prototype._getStorage = sinon.stub().returns(s); - - wf = new Identity(config,'0.0.1'); - - s.setPassphrase = sinon.spy(); - s.getSessionId = sinon.spy(); - s.setFromObj = sinon.spy(); - s.setLastOpened = sinon.stub().yields(null); + storage = sinon.stub(); + storage.getItem = sinon.stub(); + storage.setPassphrase = sinon.spy(); + storage.getSessionId = sinon.spy(); + storage.setFromObj = sinon.spy(); + storage.setLastOpened = sinon.stub().yields(null); + Identity._newStorage = sinon.stub().returns(storage); - var w = sinon.stub(); - w.store = sinon.stub().yields(null); + wallet = sinon.stub(); + wallet.store = sinon.stub().yields(null); + Identity._newWallet = sinon.stub().returns(wallet); - wf._getWallet = sinon.stub().returns(w); + profile = sinon.stub(); + profile.test = sinon.stub(); + Identity._newProfile = sinon.stub().returns(profile); + + iden = new Identity(email, password, config); }); afterEach(function() { - wf = undefined; + iden = undefined; }); + var email = 'hola@hola.com'; + var password = 'password'; var config = { - Network: FakeNetwork, - Blockchain: FakeBlockchain, - Storage: FakeStorage, wallet: { requiredCopayers: 3, totalCopayers: 5, @@ -88,30 +88,64 @@ describe('Identity model', function() { url: 'https://insight.bitpay.com:443' }, }, - + version: '0.0.1', }; - describe('#constructor', function() { - it('should create the factory', function() { - var wf = new Identity(config, '0.0.1'); - should.exist(wf); - wf.walletDefaults.should.deep.equal(config.wallet); - wf.version.should.equal('0.0.1'); + describe.only('#constructors', function() { + describe('#new', function() { + it('should create an identity', function() { + var iden = new Identity(email, password, config); + should.exist(iden); + iden.walletDefaults.should.deep.equal(config.wallet); + iden.version.should.equal('0.0.1'); + should.exist(iden.profile.test); + + Identity._newProfile.getCall(0).args[0].should.deep.equal({ + email: email + }); + Identity._newProfile.getCall(0).args[1].should.equal(password); + Identity._newProfile.getCall(0).args[2].should.equal(iden.storage); + }); + }); + + describe('#create', function(done) { + it('should call .store', function(done) { + Identity.prototype.store = sinon.stub().yields(null); + Identity.create(email, password, config, function(err, iden) { + should.not.exist(err); + should.exist(iden.profile.test); + iden.store.getCall(0).args[0].should.deep.equal({overwrite:false}); + done(); + }); + }); + }); + + + describe('#open', function(done) { + it('should call .read', function(done) { + Identity.prototype.read = sinon.stub().yields(null); + Identity.open(email, password, config, function(err, iden) { + should.not.exist(err); + should.exist(iden.profile.test); + iden.read.calledOnce.should.equal(true); + done(); + }); + }); }); }); // TODO this is a WALLET TEST! not Wallet Factory. Move it. describe.skip('#fromObj / #toObj', function() { it('round trip', function() { - var wf = new Identity(config, '0.0.5'); + var iden = new Identity(config, '0.0.5'); var original = JSON.parse(o); - var o2 = wf.fromObj(original).toObj(); + var o2 = iden.fromObj(original).toObj(); assertObjectEqual(o2, original); }); it('round trip, using old copayerIndex', function() { - var wf = new Identity(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(o)); + var iden = new Identity(config, '0.0.5'); + var w = iden.fromObj(JSON.parse(o)); should.exist(w); w.id.should.equal("dbfe10c3fae71cea"); @@ -122,8 +156,8 @@ describe('Identity model', function() { }); it('#fromObj, skipping fields', function() { - var wf = new Identity(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(o), ['publicKeyRing']); + var iden = new Identity(config, '0.0.5'); + var w = iden.fromObj(JSON.parse(o), ['publicKeyRing']); should.exist(w); w.id.should.equal("dbfe10c3fae71cea"); @@ -139,8 +173,8 @@ describe('Identity model', function() { var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; - var wf = new Identity(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(o)); + var iden = new Identity(config, '0.0.5'); + var w = iden.fromObj(JSON.parse(o)); should.exist(w); w.id.should.equal("dbfe10c3fae71cea"); @@ -152,19 +186,19 @@ describe('Identity model', function() { }); }); - describe('#fromEncryptedObj', function() { + describe('#import', function() { it('should create wallet from encrypted object', function() { - wf.storage.setPassphrase = sinon.spy(); - wf.storage.import = sinon.stub().withArgs('base64').returns('walletObj'); - wf.fromObj = sinon.stub().withArgs('walletObj').returns('ok'); + iden.storage.setPassphrase = sinon.spy(); + iden.storage.import = sinon.stub().withArgs('base64').returns('walletObj'); + iden.fromObj = sinon.stub().withArgs('walletObj').returns('ok'); - var w = wf.fromEncryptedObj("encrypted object", "123"); + var w = iden.fromEncryptedObj("encrypted object", "123"); w.should.equal('ok'); - wf.storage.setPassphrase.calledOnce.should.be.true; - wf.storage.setPassphrase.getCall(0).args[0].should.equal('123'); - wf.storage.import.calledOnce.should.be.true; - wf.fromObj.calledWith('walletObj').should.be.true; + iden.storage.setPassphrase.calledOnce.should.be.true; + iden.storage.setPassphrase.getCall(0).args[0].should.equal('123'); + iden.storage.import.calledOnce.should.be.true; + iden.fromObj.calledWith('walletObj').should.be.true; }); it('should import and update indexes', function() { var wallet = { @@ -173,26 +207,26 @@ describe('Identity model', function() { cb(); } }; - wf.fromEncryptedObj = sinon.stub().returns(wallet); + iden.fromEncryptedObj = sinon.stub().returns(wallet); - var w = wf.fromEncryptedObj("encrypted", "password"); + var w = iden.fromEncryptedObj("encrypted", "password"); should.exist(w); wallet.should.equal(w); }); it('should import with a wrong password', function() { - wf.fromEncryptedObj = sinon.stub().returns(null); - var w = wf.fromEncryptedObj("encrypted", "passwordasdfasdf"); + iden.fromEncryptedObj = sinon.stub().returns(null); + var w = iden.fromEncryptedObj("encrypted", "passwordasdfasdf"); should.not.exist(w); }); }); describe('#getWallets', function() { it('should return empty array if no wallets', function(done) { - wf.storage.getWallets = sinon.stub().yields([]); - wf.storage.getLastOpened = sinon.stub().yields(null); + iden.storage.getWallets = sinon.stub().yields([]); + iden.storage.getLastOpened = sinon.stub().yields(null); - wf.getWallets(function(err, ws) { + iden.getWallets(function(err, ws) { should.not.exist(err); ws.should.deep.equal([]); done(); @@ -200,16 +234,16 @@ describe('Identity model', function() { }); it('should be able to get current wallets', function(done) { - wf.storage.getWallets = sinon.stub().yields([{ + iden.storage.getWallets = sinon.stub().yields([{ name: 'w1', id: 'id1', }, { name: 'w', id: 'id2', }]); - wf.storage.getLastOpened = sinon.stub().yields(null); + iden.storage.getLastOpened = sinon.stub().yields(null); - wf.getWallets(function(err, ws) { + iden.getWallets(function(err, ws) { should.not.exist(err); ws.should.deep.equal([{ name: 'w1', @@ -224,16 +258,16 @@ describe('Identity model', function() { }); }); it('should include last used info', function(done) { - wf.storage.getWallets = sinon.stub().yields([{ + iden.storage.getWallets = sinon.stub().yields([{ name: 'w1', id: 'id1', }, { name: 'w', id: 'id2', }]); - wf.storage.getLastOpened = sinon.stub().yields('id2'); + iden.storage.getLastOpened = sinon.stub().yields('id2'); - wf.getWallets(function(err, ws) { + iden.getWallets(function(err, ws) { should.not.exist(err); ws.should.deep.equal([{ name: 'w1', @@ -252,19 +286,19 @@ describe('Identity model', function() { describe('#delete', function() { it('should call deleteWallet', function(done) { - wf.storage.deleteWallet = sinon.stub().yields(null); - wf.delete('xxx', function() { - wf.storage.deleteWallet.getCall(0).args[0].should.equal('xxx'); + iden.storage.deleteWallet = sinon.stub().yields(null); + iden.delete('xxx', function() { + iden.storage.deleteWallet.getCall(0).args[0].should.equal('xxx'); done(); }); }); it('should call lastOpened', function(done) { - wf.storage.deleteWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - wf.delete('xxx', function() { - wf.storage.setLastOpened.calledOnce.should.equal(true); - should.not.exist(wf.storage.setLastOpened.getCall(0).args[0]); + iden.storage.deleteWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); + iden.delete('xxx', function() { + iden.storage.setLastOpened.calledOnce.should.equal(true); + should.not.exist(iden.storage.setLastOpened.getCall(0).args[0]); done(); }); }); @@ -273,9 +307,9 @@ describe('Identity model', function() { describe('#read', function() { it('should fail to read unexisting wallet', function(done) { - wf.storage.getFirst = sinon.stub().yields(null, {}); + iden.storage.getFirst = sinon.stub().yields(null, {}); - wf.read('id', [], function(err, w) { + iden.read('id', [], function(err, w) { should.not.exist(w); should.exist(err); should.exist(err.message); @@ -285,10 +319,10 @@ describe('Identity model', function() { }); }); it('should fail to read broken wallet', function(done) { - wf.storage.getFirst = sinon.stub().yields(null, { + iden.storage.getFirst = sinon.stub().yields(null, { 'opts': 1 }); - wf.read('id', [], function(err, w) { + iden.read('id', [], function(err, w) { should.not.exist(w); should.exist(err); should.exist(err.message); @@ -298,12 +332,12 @@ describe('Identity model', function() { }); }); it('should read existing wallet', function(done) { - var wf = new Identity(config, '0.0.1'); - wf.storage.getFirst = sinon.stub().yields(null, { + var iden = new Identity(config, '0.0.1'); + iden.storage.getFirst = sinon.stub().yields(null, { 'opts': 1 }); - wf.fromObj = sinon.stub().returns('ok'); - wf.read('id', [], function(err, w) { + iden.fromObj = sinon.stub().returns('ok'); + iden.read('id', [], function(err, w) { should.not.exist(err); should.exist(w); done(); @@ -319,33 +353,33 @@ describe('Identity model', function() { }; it('should call setPassphrase', function(done) { - var wf = new Identity(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); - wf.open('dummy', 'xxx', function(err, w) { - wf.storage.setPassphrase.calledOnce.should.equal(true); - wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + iden.open('dummy', 'xxx', function(err, w) { + iden.storage.setPassphrase.calledOnce.should.equal(true); + iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); done(); }); }); it('should call return wallet', function(done) { - var wf = new Identity(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); - wf.open('dummy', 'xxx', function(err, w) { + iden.open('dummy', 'xxx', function(err, w) { w.should.equal(s1); s1.store.calledOnce.should.equal(true); done(); @@ -354,52 +388,52 @@ describe('Identity model', function() { it('should call #store', function(done) { - var wf = new Identity(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); - wf.open('dummy', 'xxx', function(err, w) { + iden.open('dummy', 'xxx', function(err, w) { s1.store.calledOnce.should.equal(true); done(); }); }); it('should call #setLastOpened', function(done) { - var wf = new Identity(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); - wf.open('dummy', 'xxx', function(err, w) { - wf.storage.setLastOpened.calledOnce.should.equal(true); - wf.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); + iden.open('dummy', 'xxx', function(err, w) { + iden.storage.setLastOpened.calledOnce.should.equal(true); + iden.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); done(); }); }); it('should call #migrateWallet', function(done) { - var wf = new Identity(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.deleteWallet_Old = sinon.stub().yields(null); - wf.storage.removeGlobal = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.deleteWallet_Old = sinon.stub().yields(null); + iden.storage.removeGlobal = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); - wf.open('dummy', 'xxx', function(err, w) { - wf.migrateWallet.calledOnce.should.equal(true); - wf.migrateWallet.getCall(0).args[0].should.equal('dummy'); + iden.open('dummy', 'xxx', function(err, w) { + iden.migrateWallet.calledOnce.should.equal(true); + iden.migrateWallet.getCall(0).args[0].should.equal('dummy'); done(); }); }); @@ -407,7 +441,7 @@ describe('Identity model', function() { describe('#createWallet', function() { it('should create wallet', function(done) { - wf.createWallet(null, function(err, w) { + iden.createWallet(null, function(err, w) { should.exist(w); should.not.exist(err); done(); @@ -416,20 +450,20 @@ describe('Identity model', function() { it('should be able to create wallets with given pk', function(done) { var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; - wf.createWallet({ + iden.createWallet({ privateKeyHex: priv, }, function(err, w) { - wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); should.not.exist(err); done(); }); }); it('should be able to create wallets with random pk', function(done) { - wf.createWallet(null, function(err, w1) { - wf.createWallet(null, function(err, w2) { - wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( - wf._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString + iden.createWallet(null, function(err, w1) { + iden.createWallet(null, function(err, w2) { + iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( + iden._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString ); done(); }); @@ -445,7 +479,7 @@ describe('Identity model', function() { }; it('should yield bad network error', function(done) { - var net = wf.networks['testnet']; + var net = iden.networks['testnet']; net.greet = sinon.stub(); net.cleanUp = sinon.stub(); net.start = sinon.stub().yields(null); @@ -456,7 +490,7 @@ describe('Identity model', function() { opts: {}, }); opts.privHex = undefined; - wf.joinWallet(opts, function(err, w) { + iden.joinWallet(opts, function(err, w) { err.should.equal('badNetwork'); done(); }); @@ -465,18 +499,18 @@ describe('Identity model', function() { it('should yield to join error', function(done) { opts.privHex = undefined; - var net = wf.networks['testnet']; + var net = iden.networks['testnet']; net.greet = sinon.stub(); net.cleanUp = sinon.stub(); net.start = sinon.stub().yields(null); - + net.on = sinon.stub(); net.on.withArgs('serverError').yields(null); net.on.withArgs('data').yields('senderId', { type: 'walletId', - networkName: wf.networkName, + networkName: iden.networkName, }); - wf.joinWallet(opts, function(err, w) { + iden.joinWallet(opts, function(err, w) { err.should.equal('joinError'); done(); }); @@ -485,7 +519,7 @@ describe('Identity model', function() { it('should call network.start / create', function(done) { opts.privHex = undefined; - var net = wf.networks['testnet']; + var net = iden.networks['testnet']; net.cleanUp = sinon.spy(); net.greet = sinon.spy(); net.start = sinon.stub().yields(null); @@ -500,11 +534,11 @@ describe('Identity model', function() { var w = sinon.stub(); w.sendWalletReady = sinon.spy(); - wf.createWallet = sinon.stub().yields(null, w); - wf.joinWallet(opts, function(err, w) { + iden.createWallet = sinon.stub().yields(null, w); + iden.joinWallet(opts, function(err, w) { net.start.calledOnce.should.equal(true); - wf.createWallet.calledOnce.should.equal(true); - wf.createWallet.calledOnce.should.equal(true); + iden.createWallet.calledOnce.should.equal(true); + iden.createWallet.calledOnce.should.equal(true); w.sendWalletReady.calledOnce.should.equal(true); w.sendWalletReady.getCall(0).args[0].should.equal('03ddbc4711534bc62ccf576ab05f2a0afd11f9e2f4016781f3f5a88de9543a229a'); @@ -514,7 +548,7 @@ describe('Identity model', function() { it('should return walletFull', function(done) { opts.privHex = undefined; - var net = wf.networks['testnet']; + var net = iden.networks['testnet']; net.cleanUp = sinon.spy(); net.greet = sinon.spy(); net.start = sinon.stub().yields(null); @@ -526,30 +560,30 @@ describe('Identity model', function() { networkName: 'testnet', opts: {}, }); - wf.createWallet = sinon.stub().yields(null, null); - wf.joinWallet(opts, function(err, w) { + iden.createWallet = sinon.stub().yields(null, null); + iden.joinWallet(opts, function(err, w) { err.should.equal('walletFull'); done(); }); }); it('should accept a priv key a input', function() { - var wf = new Identity(config, '0.0.1'); + var iden = new Identity(config, '0.0.1'); opts.privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd'; - var net = wf.networks['testnet']; + var net = iden.networks['testnet']; net.cleanUp = sinon.spy(); net.start = sinon.spy(); - wf.joinWallet(opts, function(err, w) { + iden.joinWallet(opts, function(err, w) { net.start.getCall(0).args[0].privkey.should.equal('ddc2fa8c583a73c4b2a24630ec7c283df4e7c230a02c4e48bc36ec61687afd7d'); }); }); it('should call network.start with private key', function() { opts.privHex = undefined; - var wf = new Identity(config, '0.0.1'); - var net = wf.networks['testnet']; + var iden = new Identity(config, '0.0.1'); + var net = iden.networks['testnet']; net.cleanUp = sinon.spy(); net.start = sinon.spy(); - wf.joinWallet(opts, function(err, w) { + iden.joinWallet(opts, function(err, w) { net.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer }); }); @@ -558,8 +592,8 @@ describe('Identity model', function() { describe.skip('Backwards compatibility tests', function() { it('should be able to import unencrypted legacy wallet TxProposal: v0', function() { - var wf = new Identity(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(legacyO)); + var iden = new Identity(config, '0.0.5'); + var w = iden.fromObj(JSON.parse(legacyO)); should.exist(w); w.id.should.equal('55d4bd062d32f90a'); @@ -570,14 +604,14 @@ describe('Identity model', function() { it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function() { - wf.storage.import = sinon.stub(); - wf.storage.setPassphrase = sinon.spy(); - wf.storage.import.withArgs('dummy').returns(JSON.parse(legacy1)); + iden.storage.import = sinon.stub(); + iden.storage.setPassphrase = sinon.spy(); + iden.storage.import.withArgs('dummy').returns(JSON.parse(legacy1)); - var w = wf.fromEncryptedObj('dummy', 'xxx'); + var w = iden.fromEncryptedObj('dummy', 'xxx'); should.exist(w); - wf.storage.setPassphrase.calledOnce.should.equal(true); - wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + iden.storage.setPassphrase.calledOnce.should.equal(true); + iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); w.isReady().should.equal(true); var wo = w.toObj(); From 77a01370cb8a8aa1c53a7f3086efd9d75ec99cf2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sun, 28 Sep 2014 20:50:37 -0300 Subject: [PATCH 011/182] add overwrite param to #store --- js/models/Identity.js | 23 ++++++++++++---- js/models/Profile.js | 43 ++++++++++++++++++++++------- js/models/Wallet.js | 2 +- test/test.Identity.js | 63 +++++++++++++++++++++++++++++++++++++++---- test/test.Profile.js | 43 ++++++++++++++++++++--------- 5 files changed, 141 insertions(+), 33 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 84cb41595..b774891c8 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -123,7 +123,7 @@ Identity.prototype.validate = function(authcode, cb) { */ Identity.open = function(email, password, opts, cb) { var iden = new Identity(email, password, opts); - iden.read(function(err){ + iden.read(function(err) { return cb(err, iden); }); }; @@ -143,8 +143,21 @@ Identity.isAvailable = function(email, opts, cb) { Identity.prototype.store = function(opts, cb) { - console.log('[Identity.js.142] TODO .store'); //TODO - return cb(); + var self = this; + self.profile.store(function() { + var l = self.wallets.length, + i = 0; + if (!l) return cb(); + + _.each(self.wallets, function(w) { + w.store(function(err) { + if (err) return cb(err); + + if (++i == l) + return cb(); + }) + }); + }); }; @@ -155,8 +168,8 @@ Identity.prototype.store = function(opts, cb) { */ Identity.prototype.obtainNetworkName = function(obj) { return obj.networkName || - obj.opts.networkName || - obj.publicKeyRing.networkName || + (obj.opts ? obj.opts.networkName : null) || + (obj.publicKeyRing ? obj.publicKeyRing.networkName :null) || obj.privateKey.networkName; }; diff --git a/js/models/Profile.js b/js/models/Profile.js index 61e01425a..a5831e5fc 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -10,34 +10,57 @@ function Profile(info, password, storage) { preconditions.checkArgument(storage); preconditions.checkArgument(storage.getItem); - this.email = info.email; + this.email = info.email; this.extra = info.extra; - this.hash = bitcore.util.sha256ripe160(this.email + this.password).toString('hex'); + this.hash = Profile.hash(this.email, password); this.storage = storage; }; +Profile.hash = function(email, password) { + return bitcore.util.sha256ripe160(email + password).toString('hex'); +}; + Profile.fromObj = function(obj, password, storage) { var o = _.clone(obj); return new Profile(obj, password, storage); }; + +Profile.prototype.key = function() { + return 'identity::' + this.hash + '_' + this.email; +}; + Profile.prototype.toObj = function() { var obj = _.clone(this); delete obj['hash']; return JSON.parse(JSON.stringify(obj)); }; +Profile.open = function(storage, cb) { + var key = this.key(); + this.storage.getGlobal(key, function(err, val) { + if (!val) return cb(new Error('PNOTFOUND: Profile not found')); + return cb(Profile.fromObj(val, password, storage)); + }); +}; -Profile.prototype.store = function(cb) { - var val = this.toObj(); - var key = 'identity::' + this.hash + '_' + this.email; +Profile.prototype.store = function(opts, cb) { + var self = this; + var val = self.toObj(); + var key = self.key(); - this.storage.setFromObj(key, val, function(err) { - log.debug('Identity stored'); - if (cb) - cb(err); + self.storage.get(key, function(val2) { + if (val2 && !opts.overwrite) { + if (cb) + return cb(new Error('PEXISTS: Profile already exist')) + } else { + self.storage.set(key, val, function(err) { + log.debug('Identity stored'); + if (cb) + cb(err); + }); + } }); }; module.exports = Profile; - diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 4477ef611..696107422 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -876,7 +876,7 @@ Wallet.prototype.store = function(cb) { var val = this.toObj(); var key = 'wallet::' + this.id + ((val.opts && val.opts.name) ? '_' + obj.opts.name : ''); - this.storage.setFromObj(key, val, function(err) { + this.storage.set(key, val, function(err) { log.debug('Wallet stored'); if (cb) cb(err); diff --git a/test/test.Identity.js b/test/test.Identity.js index 786fa179f..a1d96055c 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -46,6 +46,8 @@ describe('Identity model', function() { profile = sinon.stub(); profile.test = sinon.stub(); + profile.store = sinon.stub(); + profile.store.yields(null); Identity._newProfile = sinon.stub().returns(profile); iden = new Identity(email, password, config); @@ -53,7 +55,7 @@ describe('Identity model', function() { afterEach(function() { - iden = undefined; + iden = storage = wallet = profile = undefined; }); @@ -91,7 +93,7 @@ describe('Identity model', function() { version: '0.0.1', }; - describe.only('#constructors', function() { + describe('#constructors', function() { describe('#new', function() { it('should create an identity', function() { var iden = new Identity(email, password, config); @@ -110,17 +112,17 @@ describe('Identity model', function() { describe('#create', function(done) { it('should call .store', function(done) { - Identity.prototype.store = sinon.stub().yields(null); Identity.create(email, password, config, function(err, iden) { should.not.exist(err); should.exist(iden.profile.test); - iden.store.getCall(0).args[0].should.deep.equal({overwrite:false}); + iden.profile.store.getCall(0).args[0].should.deep.equal({ + overwrite: false + }); done(); }); }); }); - describe('#open', function(done) { it('should call .read', function(done) { Identity.prototype.read = sinon.stub().yields(null); @@ -133,6 +135,57 @@ describe('Identity model', function() { }); }); }); + describe('#store', function() { + + it('should call .store from profile and no wallets', function(done) { + profile.store = sinon.stub().yields(null); + iden.wallets = []; + iden.store({}, function(err) { + should.not.exist(err); + profile.store.calledOnce.should.equal(true); + done(); + }); + }); + + it('should call .store from profile and wallets (2)', function(done) { + iden.profile.store = sinon.stub().yields(null); + iden.wallets = [{ + store: sinon.stub().yields(null) + }, { + store: sinon.stub().yields(null) + }]; + iden.store({}, function(err) { + should.not.exist(err); + iden.profile.store.calledOnce.should.equal(true); + iden.wallets[0].store.calledOnce.should.equal(true); + iden.wallets[1].store.calledOnce.should.equal(true); + done(); + }); + }); + }); + + describe('#obtainNetworkName', function() { + it('should return the networkname', function() { + iden.obtainNetworkName({ + networkName: 'testnet', + }).should.equal('testnet'); + iden.obtainNetworkName({ + opts: { + networkName: 'testnet' + } + }).should.equal('testnet'); + iden.obtainNetworkName({ + publicKeyRing: { + networkName: 'testnet' + } + }).should.equal('testnet'); + iden.obtainNetworkName({ + privateKey: { + networkName: 'testnet' + } + }).should.equal('testnet'); + }); + }); // TODO this is a WALLET TEST! not Wallet Factory. Move it. describe.skip('#fromObj / #toObj', function() { diff --git a/test/test.Profile.js b/test/test.Profile.js index 7d16d09fb..4baafcea6 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -18,8 +18,9 @@ describe('Profile model', function() { beforeEach(function() { storage.getItem = sinon.stub(); - storage.setFromObj = sinon.stub(); - storage.setFromObj.yields(null); + storage.set = sinon.stub(); + storage.set.yields(null); + storage.get = sinon.stub().yields(null); }); it('should fail create an instance', function() { @@ -43,15 +44,33 @@ describe('Profile model', function() { p2.should.deep.equal(p); }); - it('#store', function(done) { - var p = new Profile(opts, password, storage); - p.store(function(err) { - storage.setFromObj.getCall(0).args[1].should.deep.equal(p.toObj()); - should.not.exist(err); - done(); - }) + describe('#store', function() { + it('should call storage set', function(done) { + var p = new Profile(opts, password, storage); + p.store({}, function(err) { + storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); + should.not.exist(err); + done(); + }) + }); + it('should use fail to overwrite', function(done) { + storage.get = sinon.stub().yields(123); + var p = new Profile(opts, password, storage); + p.store({}, function(err) { + err.toString().should.contain('PEXISTS'); + should.not.exist(storage.set.getCall(0)); + done(); + }) + }); + + it('should use overwrite param', function(done) { + storage.get = sinon.stub().yields(123); + var p = new Profile(opts, password, storage); + p.store({overwrite:true}, function(err) { + storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); + should.not.exist(err); + done(); + }) + }); }); - - - }); From 7abdc776119fb91212eb6efedd9ce6c84d4324c6 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sun, 28 Sep 2014 21:22:53 -0300 Subject: [PATCH 012/182] add #addWallet to profile --- js/models/Identity.js | 19 +++++++---- js/models/Profile.js | 12 +++++++ test/test.Identity.js | 75 +++++++++++++++++++++++-------------------- test/test.Profile.js | 27 ++++++++++++++-- 4 files changed, 91 insertions(+), 42 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index b774891c8..b26ac959c 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -144,7 +144,9 @@ Identity.isAvailable = function(email, opts, cb) { Identity.prototype.store = function(opts, cb) { var self = this; - self.profile.store(function() { + self.profile.store(opts, function(err) { + if (err) return cb(err); + var l = self.wallets.length, i = 0; if (!l) return cb(); @@ -169,7 +171,7 @@ Identity.prototype.store = function(opts, cb) { Identity.prototype.obtainNetworkName = function(obj) { return obj.networkName || (obj.opts ? obj.opts.networkName : null) || - (obj.publicKeyRing ? obj.publicKeyRing.networkName :null) || + (obj.publicKeyRing ? obj.publicKeyRing.networkName : null) || obj.privateKey.networkName; }; @@ -391,12 +393,17 @@ Identity.prototype.createWallet = function(opts, cb) { opts.version = opts.version || this.version; this.storage.setPassphrase(opts.passphrase); + + var w = this._getWallet(opts); - var self = this; - w.store(function(err) { + this.profile.addWallet(w.id, function(err) { if (err) return cb(err); - self.storage.setLastOpened(w.id, function(err) { - return cb(err, w); + var self = this; + w.store(function(err) { + if (err) return cb(err); + self.storage.setLastOpened(w.id, function(err) { + return cb(err, w); + }); }); }); }; diff --git a/js/models/Profile.js b/js/models/Profile.js index a5831e5fc..c29a3595a 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -12,6 +12,7 @@ function Profile(info, password, storage) { this.email = info.email; this.extra = info.extra; + this.walletIds = {}; this.hash = Profile.hash(this.email, password); this.storage = storage; }; @@ -44,6 +45,17 @@ Profile.open = function(storage, cb) { }); }; + +Profile.prototype.addWallet = function(walletId, cb) { + if (this.walletIds[walletId]) + return cb(new Error('WEXIST: Wallet already on profile')); + + this.walletIds[walletId] = Date.now(); + this.store({ + overwrite: true + }, cb); +}; + Profile.prototype.store = function(opts, cb) { var self = this; var val = self.toObj(); diff --git a/test/test.Identity.js b/test/test.Identity.js index a1d96055c..65e266db7 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -46,11 +46,16 @@ describe('Identity model', function() { profile = sinon.stub(); profile.test = sinon.stub(); - profile.store = sinon.stub(); - profile.store.yields(null); + profile.store = sinon.stub().yields(null);; Identity._newProfile = sinon.stub().returns(profile); iden = new Identity(email, password, config); + + var w = sinon.stub(); + w.store = sinon.stub().yields(null); + iden._getWallet = sinon.stub().returns(w); + + }); @@ -187,6 +192,40 @@ describe('Identity model', function() { }); }); + + describe('#createWallet', function() { + it('should create wallet', function(done) { + iden.createWallet(null, function(err, w) { + should.exist(w); + should.not.exist(err); + done(); + }); + }); + + it('should be able to create wallets with given pk', function(done) { + var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; + iden.createWallet({ + privateKeyHex: priv, + }, function(err, w) { + iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + should.not.exist(err); + done(); + }); + }); + + it('should be able to create wallets with random pk', function(done) { + iden.createWallet(null, function(err, w1) { + iden.createWallet(null, function(err, w2) { + iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( + iden._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString + ); + done(); + }); + }); + }); + }); + + // TODO this is a WALLET TEST! not Wallet Factory. Move it. describe.skip('#fromObj / #toObj', function() { it('round trip', function() { @@ -492,38 +531,6 @@ describe('Identity model', function() { }); }); - describe('#createWallet', function() { - it('should create wallet', function(done) { - iden.createWallet(null, function(err, w) { - should.exist(w); - should.not.exist(err); - done(); - }); - }); - - it('should be able to create wallets with given pk', function(done) { - var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; - iden.createWallet({ - privateKeyHex: priv, - }, function(err, w) { - iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); - should.not.exist(err); - done(); - }); - }); - - it('should be able to create wallets with random pk', function(done) { - iden.createWallet(null, function(err, w1) { - iden.createWallet(null, function(err, w2) { - iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( - iden._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString - ); - done(); - }); - }); - }); - }); - describe('#joinWallet', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', diff --git a/test/test.Profile.js b/test/test.Profile.js index 4baafcea6..7eb4adfad 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -44,6 +44,27 @@ describe('Profile model', function() { p2.should.deep.equal(p); }); + describe.only('#addWallet', function() { + it('should add a wallet id', function(done) { + var p = new Profile(opts, password, storage); + p.addWallet('123', function(err) { + p.walletIds['123'].should.be.above(123456789); + storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); + done(); + }) + }); + it('should keep old value', function(done) { + var p = new Profile(opts, password, storage); + p.walletIds['123']=1; + p.addWallet('123', function(err) { + p.walletIds['123'].should.equal(1); + should.not.exist(storage.set.getCall(0)); + done(); + }) + }); + + }); + describe('#store', function() { it('should call storage set', function(done) { var p = new Profile(opts, password, storage); @@ -62,11 +83,13 @@ describe('Profile model', function() { done(); }) }); - + it('should use overwrite param', function(done) { storage.get = sinon.stub().yields(123); var p = new Profile(opts, password, storage); - p.store({overwrite:true}, function(err) { + p.store({ + overwrite: true + }, function(err) { storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); should.not.exist(err); done(); From 1ff109b5e527b3d2334ec94e9776684402037d70 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 29 Sep 2014 06:31:04 -0300 Subject: [PATCH 013/182] complete API in profile --- js/models/Identity.js | 6 ++- js/models/Profile.js | 43 ++++++++++++++++++-- test/test.Identity.js | 16 ++++++-- test/test.Profile.js | 95 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 142 insertions(+), 18 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index b26ac959c..88942672d 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -395,10 +395,10 @@ Identity.prototype.createWallet = function(opts, cb) { this.storage.setPassphrase(opts.passphrase); + var self = this; var w = this._getWallet(opts); this.profile.addWallet(w.id, function(err) { if (err) return cb(err); - var self = this; w.store(function(err) { if (err) return cb(err); self.storage.setLastOpened(w.id, function(err) { @@ -453,6 +453,10 @@ Identity.prototype.openWallet = function(walletId, passphrase, cb) { }); }; + +TODO +from profile +implement lastOpen Identity.prototype.listWallets = function(cb) { var self = this; this.storage.getWallets(function(wallets) { diff --git a/js/models/Profile.js b/js/models/Profile.js index c29a3595a..581749f39 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -12,7 +12,7 @@ function Profile(info, password, storage) { this.email = info.email; this.extra = info.extra; - this.walletIds = {}; + this.walletInfos = {}; this.hash = Profile.hash(this.email, password); this.storage = storage; }; @@ -45,12 +45,47 @@ Profile.open = function(storage, cb) { }); }; +Profile.prototype.getWallet = function(walletId, cb) { + return this.walletInfos[walletId]; +}; -Profile.prototype.addWallet = function(walletId, cb) { - if (this.walletIds[walletId]) +Profile.prototype.listWallets = function(opts, cb) { + return _.sortBy(this.walletInfos,function(winfo){ + return winfo.lastOpenedTs || winfo.createdTs; + }); +}; + + +Profile.prototype.deleteWallet = function(walletId, cb) { + if (!this.walletInfos[walletId]) + return cb(new Error('WNOEXIST: Wallet not on profile')); + + delete this.walletInfos[walletId]; + + this.store({ + overwrite: true + }, cb); +}; + +Profile.prototype.addToWallet = function(walletId, info, cb) { + if (!this.walletInfos[walletId]) + return cb(new Error('WNOEXIST: Wallet not on profile')); + + this.walletInfos[walletId] = _.extend(this.walletInfos[walletId], info); + + this.store({ + overwrite: true + }, cb); +}; + + + +Profile.prototype.addWallet = function(walletId, info, cb) { + if (this.walletInfos[walletId]) return cb(new Error('WEXIST: Wallet already on profile')); - this.walletIds[walletId] = Date.now(); + this.walletInfos[walletId] = _.extend(info, {createdTs: Date.now(), id: walletId} ); + this.store({ overwrite: true }, cb); diff --git a/test/test.Identity.js b/test/test.Identity.js index 65e266db7..9b714f332 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -45,7 +45,7 @@ describe('Identity model', function() { Identity._newWallet = sinon.stub().returns(wallet); profile = sinon.stub(); - profile.test = sinon.stub(); + profile.addWallet = sinon.stub().yields(null);; profile.store = sinon.stub().yields(null);; Identity._newProfile = sinon.stub().returns(profile); @@ -105,7 +105,7 @@ describe('Identity model', function() { should.exist(iden); iden.walletDefaults.should.deep.equal(config.wallet); iden.version.should.equal('0.0.1'); - should.exist(iden.profile.test); + should.exist(iden.profile.addWallet); Identity._newProfile.getCall(0).args[0].should.deep.equal({ email: email @@ -119,7 +119,7 @@ describe('Identity model', function() { it('should call .store', function(done) { Identity.create(email, password, config, function(err, iden) { should.not.exist(err); - should.exist(iden.profile.test); + should.exist(iden.profile.addWallet); iden.profile.store.getCall(0).args[0].should.deep.equal({ overwrite: false }); @@ -133,7 +133,7 @@ describe('Identity model', function() { Identity.prototype.read = sinon.stub().yields(null); Identity.open(email, password, config, function(err, iden) { should.not.exist(err); - should.exist(iden.profile.test); + should.exist(iden.profile.addWallet); iden.read.calledOnce.should.equal(true); done(); }); @@ -202,6 +202,14 @@ describe('Identity model', function() { }); }); + it('should add wallet to profile', function(done) { + iden.createWallet(null, function(err, w) { + profile.addWallet.getCall(0).args[0].should.contain('spy#'); + done(); + }); + }); + + it('should be able to create wallets with given pk', function(done) { var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; iden.createWallet({ diff --git a/test/test.Profile.js b/test/test.Profile.js index 7eb4adfad..b4801aee2 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -1,5 +1,5 @@ 'use strict'; - +var _ = require('underscore'); var chai = chai || require('chai'); var should = chai.should(); var bitcore = bitcore || require('bitcore'); @@ -44,27 +44,104 @@ describe('Profile model', function() { p2.should.deep.equal(p); }); - describe.only('#addWallet', function() { + describe('#addWallet', function() { it('should add a wallet id', function(done) { var p = new Profile(opts, password, storage); - p.addWallet('123', function(err) { - p.walletIds['123'].should.be.above(123456789); + p.addWallet('123', {}, function(err) { + p.getWallet('123').createdTs.should.be.above(123456789); storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); done(); }) }); - it('should keep old value', function(done) { + it('should keep old ts value', function(done) { var p = new Profile(opts, password, storage); - p.walletIds['123']=1; - p.addWallet('123', function(err) { - p.walletIds['123'].should.equal(1); + p.walletInfos['123'] = { + createdTs: 1 + }; + p.addWallet('123', {}, function(err) { + err.toString().should.contain('WEXIST'); + p.walletInfos['123'].createdTs.should.equal(1); should.not.exist(storage.set.getCall(0)); done(); }) }); - + it('should add a wallet info', function(done) { + var p = new Profile(opts, password, storage); + p.addWallet('123', { + a: 1, + b: 2 + }, function(err) { + var w = p.getWallet('123'); + w.createdTs.should.be.above(123456789); + w.a.should.equal(1); + w.b.should.equal(2); + storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); + done(); + }) + }); }); + describe('#addToWallet', function() { + it('should warn if wallet does not exist', function(done) { + var p = new Profile(opts, password, storage); + p.addToWallet('234',{1:1}, function(err) { + err.toString().should.contain('WNOEXIST'); + done(); + }); + }); + it('should add info to a wallet', function(done) { + var p = new Profile(opts, password, storage); + p.addWallet('234', {}, function(err) { + p.addToWallet('234',{'hola':1}, function(err) { + var w = p.getWallet('234'); + should.exist(w); + w.hola.should.equal(1); + w.createdTs.should.be.above(123456789); + done(); + }) + }) + }); + }); + + + + describe('#listWallets', function() { + it('should list wallets in order', function(done) { + var p = new Profile(opts, password, storage); + p.addWallet('123', {}, function(err) { + p.addWallet('234', {}, function(err) { + _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '234']); + done(); + }) + }); + }); + }); + + describe('#deleteWallet', function() { + it('should delete a wallet', function(done) { + var p = new Profile(opts, password, storage); + p.addWallet('123', {}, function(err) { + p.addWallet('234', {}, function(err) { + p.addWallet('345', {}, function(err) { + _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '234', '345']); + p.deleteWallet('234', function(err) { + _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '345']); + done(); + }); + }) + }); + }); + }); + it('should warn if wallet does not exist', function(done) { + var p = new Profile(opts, password, storage); + p.deleteWallet('234', function(err) { + err.toString().should.contain('WNOEXIST'); + done(); + }); + }); + }); + + describe('#store', function() { it('should call storage set', function(done) { var p = new Profile(opts, password, storage); From abf74695b6f93b434fa0d95a2beb14045a7b4617 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 29 Sep 2014 10:18:47 -0300 Subject: [PATCH 014/182] mv wallet test from walletfactory to wallet --- js/models/Identity.js | 46 +++++++++++----------------- js/models/Profile.js | 30 ++++++++++++------- js/models/Wallet.js | 40 +++++++++++++++++++++++-- test/Wallet.js | 58 +++++++++++++++++++++++++++++++++++ test/test.Identity.js | 70 ++++++++++--------------------------------- 5 files changed, 150 insertions(+), 94 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 88942672d..395aba51a 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -142,6 +142,13 @@ Identity.isAvailable = function(email, opts, cb) { }; +/** + * store + * + * @param opts + * @param cb + * @return {undefined} + */ Identity.prototype.store = function(opts, cb) { var self = this; self.profile.store(opts, function(err) { @@ -319,7 +326,7 @@ Identity.prototype.read_Old = function(walletId, skipFields, cb) { */ -Identity.prototype._getWallet = function(opts) { +Identity.prototype._newWallet = function(opts) { return new Wallet(opts); }; @@ -396,12 +403,12 @@ Identity.prototype.createWallet = function(opts, cb) { var self = this; - var w = this._getWallet(opts); + var w = this._newWallet(opts); this.profile.addWallet(w.id, function(err) { if (err) return cb(err); w.store(function(err) { if (err) return cb(err); - self.storage.setLastOpened(w.id, function(err) { + self.profile.setLastOpenedTs(w.id, function(err) { return cb(err, w); }); }); @@ -445,7 +452,7 @@ Identity.prototype.openWallet = function(walletId, passphrase, cb) { if (err) return cb(err); w.store(function(err) { - self.storage.setLastOpened(walletId, function() { + self.profile.setLastOpenedTs(walletId, function() { return cb(err, w); }); }); @@ -454,42 +461,25 @@ Identity.prototype.openWallet = function(walletId, passphrase, cb) { }; -TODO -from profile -implement lastOpen -Identity.prototype.listWallets = function(cb) { - var self = this; - this.storage.getWallets(function(wallets) { - wallets.forEach(function(i) { - i.show = i.name ? ((i.name + ' <' + i.id + '>')) : i.id; - }); - self.storage.getLastOpened(function(lastId) { - var last = _.findWhere(wallets, { - id: lastId - }); - if (last) - last.lastOpened = true; - return cb(null, wallets); - }) - }); +Identity.prototype.listWallets = function() { + return this.profile.listWallets(); }; /** * @desc Deletes this wallet. This involves removing it from the storage instance - * @TODO: delete is a reserved javascript keyword. NEVER USE IT. * @param {string} walletId - * @TODO: Why is there a callback? * @callback cb - * @return {?} the result of the callback + * @return {err} */ Identity.prototype.deleteWallet = function(walletId, cb) { var self = this; - self.storage.deleteWallet(walletId, function(err) { + + Wallet.delete(walletId, this.storage, function(err) { if (err) return cb(err); - self.storage.setLastOpened(null, function(err) { + self.profile.deleteWallet(walletId, function(err) { return cb(err); }); - }); + }) }; /** diff --git a/js/models/Profile.js b/js/models/Profile.js index 581749f39..a2a675b30 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -45,19 +45,19 @@ Profile.open = function(storage, cb) { }); }; -Profile.prototype.getWallet = function(walletId, cb) { +Profile.prototype.getWallet = function(walletId, cb) { return this.walletInfos[walletId]; }; -Profile.prototype.listWallets = function(opts, cb) { - return _.sortBy(this.walletInfos,function(winfo){ +Profile.prototype.listWallets = function(opts, cb) { + return _.sortBy(this.walletInfos, function(winfo) { return winfo.lastOpenedTs || winfo.createdTs; }); }; -Profile.prototype.deleteWallet = function(walletId, cb) { - if (!this.walletInfos[walletId]) +Profile.prototype.deleteWallet = function(walletId, cb) { + if (!this.walletInfos[walletId]) return cb(new Error('WNOEXIST: Wallet not on profile')); delete this.walletInfos[walletId]; @@ -67,8 +67,8 @@ Profile.prototype.deleteWallet = function(walletId, cb) { }, cb); }; -Profile.prototype.addToWallet = function(walletId, info, cb) { - if (!this.walletInfos[walletId]) +Profile.prototype.addToWallet = function(walletId, info, cb) { + if (!this.walletInfos[walletId]) return cb(new Error('WNOEXIST: Wallet not on profile')); this.walletInfos[walletId] = _.extend(this.walletInfos[walletId], info); @@ -80,17 +80,27 @@ Profile.prototype.addToWallet = function(walletId, info, cb) { -Profile.prototype.addWallet = function(walletId, info, cb) { - if (this.walletInfos[walletId]) +Profile.prototype.addWallet = function(walletId, info, cb) { + if (this.walletInfos[walletId]) return cb(new Error('WEXIST: Wallet already on profile')); - this.walletInfos[walletId] = _.extend(info, {createdTs: Date.now(), id: walletId} ); + this.walletInfos[walletId] = _.extend(info, { + createdTs: Date.now(), + id: walletId + }); this.store({ overwrite: true }, cb); }; + +Profile.prototype.setLasOpenedTs = function(walletId, cb) { + return this.addToWallet(walletId, { + lastOpenedTs: Date.now() + }, cb); +}; + Profile.prototype.store = function(opts, cb) { var self = this; var val = self.toObj(); diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 696107422..fedd7a4e1 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -57,6 +57,9 @@ var copayConfig = require('../../config'); */ function Wallet(opts) { var self = this; + preconditions.checkArgument(opts); + + opts.reconnectDelay = opts.reconnectDelay || 500; //required params ['storage', 'network', 'blockchain', @@ -177,6 +180,25 @@ Wallet.getMaxRequiredCopayers = function(totalCopayers) { return Wallet.COPAYER_PAIR_LIMITS[totalCopayers]; }; +/** + * delete + * + * @param walletId + * @param storage + * @param cb + * @return {undefined} + */ +Wallet.delete = function(walletId, storage, cb) { + preconditions.checkArgument(cb); + + storage.deletePrefix('wallet::' + walletId, function(err) { + if (err) return cb(err); + storage.deletePrefix(walletId + '::', function(err) { + return cb(err); + }); + }); +}; + /** * @desc Set the copayer id for the owner of this wallet @@ -874,7 +896,7 @@ Wallet.prototype.store = function(cb) { var self = this; this.keepAlive(); - var val = this.toObj(); + var val = this.toObj(); var key = 'wallet::' + this.id + ((val.opts && val.opts.name) ? '_' + obj.opts.name : ''); this.storage.set(key, val, function(err) { log.debug('Wallet stored'); @@ -919,9 +941,23 @@ Wallet.prototype.toObj = function() { * @param {Storage} storage - a Storage instance to store the data of the wallet * @param {Network} network - a Network instance to communicate with peers * @param {Blockchain} blockchain - a Blockchain instance to retrieve state from the blockchain + * @param skipFields */ -Wallet.fromObj = function(o, storage, network, blockchain) { +Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { + + if (skipFields) { + _.each(skipFields, function(k) { + if (o[k]) { + delete o[k]; + } else { + throw new Error('unknown field:' + k); + } + }); + } + + // TODO Why moving everything to opts. This needs refactoring. + // clone opts var opts = JSON.parse(JSON.stringify(o.opts)); diff --git a/test/Wallet.js b/test/Wallet.js index 8fd2fa218..400e4c3b1 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -10,6 +10,16 @@ var TransactionBuilder = bitcore.TransactionBuilder; var Transaction = bitcore.Transaction; var Address = bitcore.Address; + +function assertObjectEqual(a, b) { + Wallet.PERSISTED_PROPERTIES.forEach(function(k) { + if (a[k] && b[k]) { + _.omit(a[k], 'name').should.be.deep.equal(b[k], k + ' differs'); + } + }) +} + + var walletConfig = { requiredCopayers: 3, totalCopayers: 5, @@ -1806,4 +1816,52 @@ describe('Wallet model', function() { }); }); + describe('#fromObj / #toObj', function() { + var storage = new Storage(walletConfig.storage); + var network = new Network(walletConfig.network); + var blockchain = new Blockchain(walletConfig.blockchain); + + it('Import backup using old copayerIndex', function() { + var w = Wallet.fromObj(JSON.parse(o), storage, network, blockchain); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + assertObjectEqual(w.toObj(), JSON.parse(o)); + }); + + it('#fromObj, skipping fields', function() { + var w = Wallet.fromObj(JSON.parse(o), storage, network, blockchain, ['publicKeyRing']); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + (function() { + assertObjectEqual(w.toObj(), JSON.parse(o)) + }).should.throw(); + }); + + it('support old index schema: #fromObj #toObj round trip', function() { + var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; + var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; + + var w = Wallet.fromObj(JSON.parse(o), storage, network, blockchain); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj); + should.exist(w.privateKey.toObj); + + assertObjectEqual(w.toObj(), JSON.parse(o2)); + }); + }); + + // DATA + var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; + }); diff --git a/test/test.Identity.js b/test/test.Identity.js index 9b714f332..db8e6a4b8 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -37,6 +37,7 @@ describe('Identity model', function() { storage.getSessionId = sinon.spy(); storage.setFromObj = sinon.spy(); storage.setLastOpened = sinon.stub().yields(null); + storage.deletePrefix = sinon.stub().yields(null); Identity._newStorage = sinon.stub().returns(storage); @@ -46,6 +47,8 @@ describe('Identity model', function() { profile = sinon.stub(); profile.addWallet = sinon.stub().yields(null);; + profile.deleteWallet = sinon.stub().yields(null);; + profile.setLastOpenedTs = sinon.stub().yields(null);; profile.store = sinon.stub().yields(null);; Identity._newProfile = sinon.stub().returns(profile); @@ -53,7 +56,7 @@ describe('Identity model', function() { var w = sinon.stub(); w.store = sinon.stub().yields(null); - iden._getWallet = sinon.stub().returns(w); + iden._newWallet = sinon.stub().returns(w); }); @@ -215,7 +218,7 @@ describe('Identity model', function() { iden.createWallet({ privateKeyHex: priv, }, function(err, w) { - iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + iden._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); should.not.exist(err); done(); }); @@ -224,8 +227,8 @@ describe('Identity model', function() { it('should be able to create wallets with random pk', function(done) { iden.createWallet(null, function(err, w1) { iden.createWallet(null, function(err, w2) { - iden._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( - iden._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString + iden._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( + iden._newWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString ); done(); }); @@ -234,58 +237,18 @@ describe('Identity model', function() { }); - // TODO this is a WALLET TEST! not Wallet Factory. Move it. - describe.skip('#fromObj / #toObj', function() { - it('round trip', function() { - var iden = new Identity(config, '0.0.5'); - var original = JSON.parse(o); - var o2 = iden.fromObj(original).toObj(); - assertObjectEqual(o2, original); - }); - - it('round trip, using old copayerIndex', function() { - var iden = new Identity(config, '0.0.5'); - var w = iden.fromObj(JSON.parse(o)); - - should.exist(w); - w.id.should.equal("dbfe10c3fae71cea"); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj()); - should.exist(w.privateKey.toObj()); - assertObjectEqual(w.toObj(), JSON.parse(o)); - }); - - it('#fromObj, skipping fields', function() { - var iden = new Identity(config, '0.0.5'); - var w = iden.fromObj(JSON.parse(o), ['publicKeyRing']); - - should.exist(w); - w.id.should.equal("dbfe10c3fae71cea"); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj()); - should.exist(w.privateKey.toObj()); - (function() { - assertObjectEqual(w.toObj(), JSON.parse(o)) - }).should.throw(); - }); - - it('support old index schema: #fromObj #toObj round trip', function() { - var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; - var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; - - var iden = new Identity(config, '0.0.5'); - var w = iden.fromObj(JSON.parse(o)); - - should.exist(w); - w.id.should.equal("dbfe10c3fae71cea"); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj); - should.exist(w.privateKey.toObj); - - assertObjectEqual(w.toObj(), JSON.parse(o2)); + describe('#deleteWallet', function() { + it('should call profile and wallet', function(done) { + iden.createWallet(null, function(err, w) { + iden.deleteWallet(w.id, function(err) { + should.not.exist(err); + done(); + }); + }); }); }); + describe('#import', function() { it('should create wallet from encrypted object', function() { iden.storage.setPassphrase = sinon.spy(); @@ -708,7 +671,6 @@ describe('Identity model', function() { }); -var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; var legacyO = '{"opts":{"id":"55d4bd062d32f90a","spendUnconfirmed":true,"requiredCopayers":2,"totalCopayers":2,"name":"xcvzxcv","version":"0.3.2"},"networkNonce":"53d25e8600000009","networkNonces":[],"publicKeyRing":{"walletId":"55d4bd062d32f90a","networkName":"testnet","requiredCopayers":2,"totalCopayers":2,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":4,"receiveIndex":2},{"copayerIndex":1,"changeIndex":5,"receiveIndex":2}],"copayersBackup":["02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5"],"copayersExtPubKeys":["tpubD94LTzAUiW99mpA59nyf6fAHh4xKGmnwbgCV4gU2bRpeN9CRiMSurqme22px5NmJAo6FdcdH883Zu98VbqyhesCJ86kUEjH3Zpufy5FfcaC","tpubDA2U9H6LkRHDRbRxHBp4VTbxPc7JqsvtcLxrE5QJF8z1iT6hMJ1pXSVf57GWRcxXutYvpoXRurDVGsscJauMtnJBkYAWBVExYmm91XQE2zz"],"nicknameFor":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":"asdf","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":"qwerqw"},"publicKeysCache":{"m/0/0/0":["028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90","0332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec8"],"m/1/0/0":["0220ad514cf593d0c3905d3bb49bc5767a9410823bf9b77ea5ef2cf1d1016d77a8","02fd42cf66f1dbdc7bbb9ae09aecea72df479ffe5a0c4641301067e331d12e416d"],"m/1/0/1":["0315f7868eaf1f9b7127e3f7e0222c5e473eea003e34700f4758b6873c525d6723","02a2e8ed5e90dd39e3842fc790e06178997dbca319987f365317589e2a71a93658"],"m/0/1/0":["0244a25a0b97b26707fd855c15b046b901be85a3b70a781d0678608e633440eeca","0358cdcbc528ddfb7173b0dab283f702be82546ff031e4a832a7270080cb875959"],"m/0/1/1":["025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f","020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d"],"m/1/1/0":["02fd0e7c62b7b58d1ea7bb4cb84d53b019df99d3703a42aed73a2cfa15f3af5d08","0355a15912e76072ef50e6643376b8a9da8422ed4f8ea07b1d84d4989be5a39b2e"],"m/1/1/1":["03bc3e1f4db32efd8eb1fd44a1665938d59628429c67e1e8b7054ab5717f4e6750","03c4c817b633ac31f44f16f390af831d35f7d98744a52a0f23e9598967342255f8"],"m/1/1/2":["02826fe7e9da408480ddeb1d4414c5100b350f862ca718e27122681e1a0ca35077","02bd25af907bb3edbf6b2cd1ea90eaa92cc93ec47bea7d339af44c1d2c05708e99"],"m/0/1/2":["0337a1a70364b94745d6e26d2d28919cf528304f52765f12ef43e3d6da0a6c8dc0","039d83db9aa43e6e00e0304e6971b6079d79dc12d8d55ce2e6fc24a52ba8d41329"],"m/0/0/1":["0359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b8138","037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d93"],"m/1/1/3":["02600e5c41670773a213a4cb58c8f2fa3e83840784bc7f0b56925e1075e06632c2","036d01867af5f61371151ef7d9026fa0400a623f6924e404ee0b856625268972f9"],"m/0/1/3":["03e5a9b039b187ca8e065627df402e4a5b196b94198542da7036879de08be63d2d","0304f3e0b70f696d80e5785dc7747d6dcb55ba24c31f2d80bf184b4e582e6b47fc"],"m/1/1/4":["03741afa5bd50d6ba5801064c810fae84f6a4557d6a88ddc8591d0d4eb68a8fc41","0214dd6ce6073b05999fb887098ca6f7e1d0b4fdc0760557786907df353df90d1c"],"m/2147483647/1/0":["033e072a53ea835763a03c66e35c35384736210a1bb7d7ee6d9a3e109e82426b30","02e37b5570c053da8a8ee587be86fc629775c4db890aba2745ccc4e4dcc8c31041"],"m/2147483647/1/1":["0228a6de42ef421c263d1efd9f28d9a7d15a261995028a24eff6b9f1c3fc46e6bf","0226cff885cb0d607cc9cf69a7608316eb3fb2ec344c0c9956246ba776116fc396"],"m/2147483647/1/2":["034fe2a8f0b98445eb5810fe36572ad2f64ed9bf64dc9de624f99c0142cb07c682","02f2c5c758e32293f5c193fd69afadbba83abafb397db01e6f2b447690e900475a"],"m/2147483647/1/3":["02b25ef9434446c51f10678f787e4913de582e34d164bd3b06af7732c5476df1a8","025d51a1efd59bcff22ee2e0af61b21a7ba5f639e20dfdf25690e926005177dd0c"],"m/2147483647/1/4":["03e5734e1d29b2f684d0446b7a2ffbd0ba8952570a502d0d14b1efd8f24b61be53","0258fc28a324848d8d0154e8614815e35c668d274a8f01957bb99aab8dc8f386c0"],"m/2147483647/1/5":["021f9e775246765e1cfba0ae453b4eae6cd4ae5a57a09c319edbe89d4dbbf23be3","02857f66571a1c3eb9e72d22ae88e734c03d448bced4dcfd345c2059468124c741"],"m/2147483647/1/6":["02c072f329391a25255dc6452e5f5220966869dbf736ba8a8c3ae9d273a84bc3fd","030920a8b8e88c4db2871a7df0878a86cf0695f6d96bb50c701c3454f3df25176a"],"m/2147483647/1/7":["036bf329fc19bce10cf1999fae5bfa80290ff7b44776b49c7b0dc9eec6cffcfa21","03955a549875b4f7b9be28b9ff4bcd51ad2bc224430b1634baef890585885d5e1b"],"m/2147483647/1/8":["024879c9c9a261b3141ecfa1c79c4efc25278c844ecd1dcfcb95d9c19581fbdd25","03fb4a5fdb91239df3ccf7f61a5b99e7e72483101e21c9d1ee0d85544e9354c6c7"],"m/2147483647/1/9":["035928a107ec01f78cd586914d5a49710fd42e352b1312e3ad0eeb2c9666fdf8e7","03a54c03093797854829c75357f092356352a109042bbb83bdac20cb4e5eca27ea"],"m/2147483647/1/10":["021e7a3a7efe888c5e820b5cf0f03317b2b4bf438d8563449aeb7a77cade97f136","03ec0960b3d1df52ca3cc2c82b7d97063400da4dd051bba2f9bab6cb44aee01efa"],"m/2147483647/1/11":["035d70c26b7f429861f555f7c0d99947411b23b7f95303fb8d5de5b82a95aa30fc","038b922f7024f5446d6b48e5253643543b35c006d90fd37688105c6cefcd8adb8a"],"m/2147483647/1/12":["02158d6503891c6c65a606221dbf5c68d0832288975914007968419939588ecb24","0248264cb1763a3f4de9b34787b4bc5443ec92ef915927494bb9f1c1c0b498c7ca"],"m/2147483647/1/13":["0349965eea38a25ae0c061faeac4c4e57e648bc4c0f059d07b3b8b7962cbc0dde5","0352243d9269565ce2a1ffdd0b8e43a442c6dd1c9edda86eaaf2cba5a4a95c40f1"],"m/2147483647/1/14":["030fa6e3d0c5cedc0581955395c77cbe134c912a47971023b9695332df3f7bb200","03f2cf09e33326fb59bf3f13e6298d2d5d29c9eae3b872e5a851e8d8d77259c883"],"m/2147483647/1/15":["02bf0d45e41339f552df6f8baf4392142921fd38b0f2a4388a905ff6cbacbc278a","03fabe46bb6706a1b8edfd28c046a8891b4530bbe5305080b72b0d08ebdf7b8c0a"],"m/2147483647/1/16":["03a4e3146ed34d6a8af4e4379e6edcff32cb0373ba232b3d746af3052f674133ac","030311b73c6f5c46ddffc0cfce6e5ed0b671d94267d8e52cd8837f2a479916eb91"],"m/2147483647/1/17":["03233df93c762d2f06c7f5f388e4e0a8dbdb13302acba0d2d6995c487d8aec9f2f","024badfdcb7e772ac7fc1c46d3943b07500edbbece105cdeff3eb9e9fcc9f54782"],"m/2147483647/1/18":["0364035475a098e00eb010c500cad3c90af3e81a4bd613144bc9433a150f14718b","028223dc8142154e7477ce000b3dc13e1d15a901553d9b18864c8645b582b38fe6"],"m/2147483647/1/19":["03971b74b4ac4bdaadf636baa4caa82fe5355471ed6ea05a9cbe5fc6c9e4b9db76","0202ebffacd01f83849e5bc5c0e2c317bc5fb2fbcb2d6d4482a5235f9f1308b61a"],"m/0/1/4":["03005ee9ff028c98fd132e531023f2f2b61ff0d26022f979dd98088d2ba167b031","0345ea82e8dfe38277f0c3aee18d2dd93edb63e8663ac83328a7934d2ca57006f6"],"m/0/1/5":["0391bc4990b71d8a3f156ae7107929ed6372b0b4ba8a868253f71ba7189d1efa02","0312a74cf2e7c0dd41897d04fabfd8cc3187b84a28305cfc79315b24e6fe23a6b4"],"m/0/1/6":["021a38c492607ff9684a4fec445e47b5b7100d3ef9e9dc0d0b37c0a646d28d4f77","03ae0b46ab36f97447ebaa53f2b5c8f090f15395378785f2fd285eeba17fbf3f65"],"m/0/1/7":["0308cdec88c1ffe16edc98853d9c08dbd4ba2541ba566668ca17bda19d7eb3481f","02dd622267c2e68287287b8b61724f76fbe84096a56aa5054af92f8fe25380e2d1"],"m/0/1/8":["039647da9ad725836bcb28a3e0497659a28d7749d1416c421a0a01c62d237ee962","022e22aa61eafda0dd8820427f1a06314d352a15ea8645e7ab9b80920017084d82"],"m/0/1/9":["03a4ade946076c6962b70c70ac7fad3a87efb59a1d0a4e32bda13a6d47fe9df961","029a07235aba04ab69526e117d836d5b3fae5cfc8c5e72b10c6d1afd261ccc19f3"],"m/0/1/10":["03c78e9b6493b22790db1acea20df9444e0f9c424fc5756e7a32c290ae01783953","0254c130ee467a96570c9f5ebea89de04f0b1db1686b164f2694339bef8f25dd88"],"m/0/1/11":["03a762c43318ef8d4840fab04c8db73797dc648825fac60f2730b4c76678df1cf3","0212c684a4de8e750ad2dfe2b136370ab9803eca178ed9a27b3990c29b067de35c"],"m/0/1/12":["02702d221f9b15c5cf75ac2f497a6c63e60213087c3d2d3be46768e3ebd238e26e","03ed58580744deb357258e44548212038670769d8d51e385d4fb8414311fd01b52"],"m/0/1/13":["0320e0597b54c62768352f433389cee4725d6094d7bcb5c72265edcc0933829aff","02c5706f11b9a85f3176c572842b7c9812c2195058d24d945bc026b00312740e76"],"m/0/1/14":["02fe43077676b844226d3aaa62e8a86d237710d92f882366944acbde0c8992fcaf","039a6a8662abb8910741cf331320549665e9feb28ca94d1ab6a43c84fa330b94ee"],"m/0/1/15":["0369f99f72847af93d50ab8ee75b6e7e912d26e27be96f6d6b7215cf7daeff7ba5","02521700cc07c953ba5aa586fb0e4795a34dffc68c5fb43e038be3866e40f4daed"],"m/0/1/16":["02f67d1d89bd8fe2f91c5b973cbdacfb4ba440e7656bce284cf73d549625607347","035da9cfac5a803dcb2b283b02a2515a4a1bcbf3d19e0d180aee8fc30193bc0555"],"m/0/1/17":["02c024ec199d240e8d6c66276b94b91071f7cdf2bef540c29d6d18d25de7b1cf7c","02190865f9dafae3f7f05c093463be5632946422ddda0a6fef6904390792516067"],"m/0/1/18":["035ed504d7704ad984a333b8eb0fceb8be043da9284de31ed84d9e68d90c75507d","033303c415b50421732402df00f4baa219f334647a7eb5014b9f8079864d6ab558"],"m/0/1/19":["02ce49fe86b0eee73663b1ee867b16b97c876af26f12764c528a2e6d0eb55ad3d7","03ab969bc81796b88e44c340d854df955fc60ea17ea92db5d3115595d6dec890d8"],"m/0/1/20":["03e2fa915378cbdffa0d919b0fb50c7256ca731b9d571b3365e486893a1d43079c","038d058b895cf084dccfcc9367e4796a5cf4ddceed6c35f6885d75c80119613350"],"m/0/1/21":["02fcb1bf644446b5b42205272af72f0aeab9e92ca29aafa91c5fb69142764017aa","035c5fe5c8811603279a5b72b6c30735d702817db1eab937c622269e28192ffa90"],"m/0/1/22":["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5","022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"],"m/0/1/23":["03f40b82fe8cacff08879f13c45f443a3dc3ea98e1d75d5f32a19f5e5a8f7a905b","028415ee458e4dcfd440ce969726f3b58ae74fb6cf3995ced099579211e7419844"],"m/1/1/5":["032748a6282e21f571b8c8dd49e775deb83c90fcf88dc4ba81d878536973709c3f","020837cd68f14ce571b335eecd1b6fa0af43e1576dd9721aaca2a8ab639ac6b7cd"],"m/1/1/6":["0337032efb013dc92bb8dccfbdda9f5c28f0039a9c60953d41003d095e9f9778af","03ceed2da6b9603297061dc8eb930112ba726b2ccf5eec67f4866a05ca4049a22b"],"m/1/1/7":["0383c96ac2af7d203f69133b2fab6b68366b5075ad6957fa06759df3b20fbfec70","0311385f79834cedaf2230a48c0f9dc8e794da1869fc595db2518d62debb85579a"],"m/1/1/8":["03efc649680280f4e4df96da923bc88330275004125ebe5483c2f3e05ca52e19a4","02803c02d197d780388259afbd001ae41fa3eb3e2bac9627aff540521c184c3b23"],"m/1/1/9":["03af2fe6aa027a76b42c1c4050a040bfd026ad2daec1bb96a5fe2d026a7df919de","02ce14163047c640228796fb1f72bbe3afb05819ad141598a4f021058a6f79dd3b"],"m/1/1/10":["033770378bd762cf0408e44e4e604bef77e336170428c506949b1a4f1f2963e574","02c58ed43946f699dbd3e36d3e9aab2714cadeb19ecd3a56e4328c50336b4a76cb"],"m/1/1/11":["02898a1545fa19bdca92adc498698d27b86529cd4c08946d9d29604734b86f31af","02b402767a045ede072600924401c0d720000b2ed59fa444bfdbef4a5f1cead745"],"m/1/1/12":["039b8659430be49913e2cd869aa8c99ccf49a13df35837370b792033dadb891483","03264e63df292257cc76babb15d15bef620d1c2f8c3bbc78d6ea02d127e5ee7386"],"m/1/1/13":["02381a559791b8e86bf546e2c718ae63cf24eed0518a58e4d4a4b310adf2cd38fa","02d7f8283a4418d912508901b4a3db0d2103206dfdd74b3c75648671e20ecfd445"],"m/1/1/14":["020376e8c550b7d9faa0b2da947a2a36fab22c6e8190b6f99460b6022017bb97d7","03fbc5299190e6628de28c92aaa12e3a131b21eb7266462c46fbedeb86fa878055"],"m/1/1/15":["027209fd3b0cf7368180a5dbb16b928c997d33fccb78505d48440c7d23eadf5460","03450bfb22858726cd7e228e6733f69457546978a95188565c53e0d1c0d6070ea8"],"m/1/1/16":["03cb355ba04f64293793855121bab5831f84a3a3edf7cd31fccaa6d67c407a4912","028bc897a39c1224610b765a80f4cd8ab79cb37776f58fec9c10ac6f649d1f3c72"],"m/1/1/17":["03f4cb0564d7e2c6b85673503b7954db22779f29a8f3374904573984e318a96bf1","037c11b6ee906d84aa7eed359d758d986d912b6f8e5cbb1acf0982a77b3ef812c4"],"m/1/1/18":["02d2e5798f33f6889472857744316f2d253f25f88379610063f40cfe5798d9858f","0253cefdfe9ca987cbf1c950b6246d5b7a194d8dfad47c3a78dbbc5c1d01511d97"],"m/1/1/19":["0336c325f5aed366ffc10d553f2bfd4d69e66cbe1688d77af14efc8827aea2e318","0378b1b9a6074f9f2ab4fa9ad1e14649c621b0c8124a1b148914d3c10e6ab390c6"],"m/1/1/20":["03ea55740a734689ce778a8c00df8ebf4274c8f66de7d05646fe5c927773ff7f2e","02275b558d49aef955b6dee51a3c0a53f4b076b97bb3f26abcc82540168ec87cac"],"m/1/1/21":["03c77869c9984664eac9c238f4b6d806c9f48ca8a736c48450f398834db2aa915c","02d984f548c7f60c09dad3287cfc48807bc8157123989636c713be61be6a2e9ced"],"m/1/1/22":["03ed7c6a3c854c1f9459891691cc32671402f9e47126919878251e568dbdf353f8","02a113dab22cd9e46967b3fd76b9b9ec1d227d88817a9300e42d332cca2a0877fd"],"m/1/1/23":["02ee186432dcf69fda50a6fdbd94651817d8a271c273a5b70cab3ec4ae77a3753b","02291370aad9de0dac676355ced64e268b0c431a51f42f12d13f5144940fce4285"],"m/1/1/24":["02bf71435e84e66547c8c583d5ba226a5ac4d935e0a9f9603ecd8925c3e847e91a","03578d8657d285a89d9d597632db662cfef9baccfb55c76b1e87948a94fc9de30d"],"m/2147483647/0/0":["02a8425bbe23426219065969f695a6c3e242b24e57226bffdd542be8fd6be968c9","03057a42fdb6569fb1615b173ccb702453db2eac5be4291b82d4511461eafbed87"],"m/2147483647/0/1":["0250c3d3e86e332010c5233c2ec3bc728026002f0037cb3382d6318409b0e70796","02cfac1e7c4c88191201080f8316af52d9faa6ba624a6e160279e9fac4d1cf79a9"],"m/2147483647/0/2":["02a8c266a5b92eb50c8be91f95e4d1ad968b2f57d527377fd642d63fb84474f61a","028cc954ab31bd179ff80b8a05f95430ae534e61b3ff35f5284fa2fbe1832ceccb"],"m/2147483647/0/3":["02f719e1a7ab00ea98611453fb03d44c1da04655bed74af392534d70099039b4c2","03bfa548bfd4718c50bfce173f780eadcfb679d9c0206c91a2fa1879a9cf7558b2"],"m/2147483647/0/4":["0362c0695d397ca26bf47f0e641bb3cfb06ff29ccac2e1d56ded3afcf88b1e688d","02f9d87b05bdb3b9e82f506b43f813041c0e403274adc23d11e5e1651e34b606c2"],"m/2147483647/0/5":["033731323032d4ee08e858fc71f93970444333e183a1d5052e1d08cfb511e262c8","023e12556cef67ade35b7758916b5e1a3ebe074ccd35c5d8eff6b01321f63eb495"],"m/2147483647/0/6":["025d11b90081972bc1c258c9d6f476dfc2f95b69f0e9935322bf9c21deb580ff64","02b065f56a378907354f0738a0ed74f10660c6b5dd68c9f992093b75ce3d7d8b72"],"m/2147483647/0/7":["0210e721e8a35db9d8c855a0d346f60c09208f3be80b39e03af2c29db777332c71","0277f352969fadb1f1835f9a0fa99c6a3c7b6c281be5b2794c88a708eb177ea33d"],"m/2147483647/0/8":["02998d8d41e4215cd2a961a415a3ed0b1f984f1627719a7b102a75864943c4d87b","03d8ed7fc8f68a77f68d3afd007b7aa4c89944195143630ce183f0fa5438f2b559"],"m/2147483647/0/9":["0324fa91737588e4f85937303ce65c3b91b5f2ae506a72d92b83e3f5f9aeeb3c6f","02a011be72c4a400319212228106af278823a97acfe0a67e1ecd866d446b315114"],"m/2147483647/0/10":["025886ba287922a904881c7315e6fcc410a7976741771a5937d3a1a01b529f21fd","0243bb91ceed9d29d0c2ca66a8ab77e82110bbcc023beb4106f787964f44a0b972"],"m/2147483647/0/11":["0369d21684894cc2d4b2f5e581ede3cac9e8db4161a08e7737c1be129bb673d3d5","03c9ef27e3cd3dadc078fdfd9936a7ad9bf7954747085cf8f8a2a5bb3431f68a9f"],"m/2147483647/0/12":["03a73b8fd859bf6acebffdfffa2597199091daedd2c011ac67fc3494d8a1a8ceb6","025a213f7771c8be03f43f2e7f469ad4ef2cf6907ea284b227a786d1f55dfa7144"],"m/2147483647/0/13":["03a09f7ca257e1ab263cd5e6b0addc3ff868b93df132321d98775ca3505efb576f","03454c715739164bc55f347a651439cdf3ec146b35d2927beb60e8290b3916e082"],"m/2147483647/0/14":["03a64b1f7bd94a6b1a6e84ea444e0ba04e9deb86460934ccc37c0615a134a8257b","02794f09210b1811a455f3e1c7bcd35c76dff2523190fef9615eb27e2376acac1c"],"m/2147483647/0/15":["0392dca2fd9a3bc2b2a7d90a848719069fbc5f22bff7327bb8186c032514085263","032ee8a33ea76d70c7ae839448ca6c5b1af89146f2922e23ba1822df42dbc7e66a"],"m/2147483647/0/16":["031a22a1a3c1abad7c4d782ef6ba3cc00f2e8fe549eb33e0732200aff6d3174831","03bdce9781289e0c31cf727f4c93fe46f7930dd8fd68f818ce241f1ede268e8e0e"],"m/2147483647/0/17":["03b12d27e9aea2c2ad598e54e40860a705ac2ca2427aa511b501b38ec368ea5c7d","03e60d35d84d4536cad895215256b312bb4879a8d417251c279995e58f25da3d54"],"m/2147483647/0/18":["0380266cc9a9673676ad6a1b2e7148766df9c25b4dce299e5edc4f65b72aa58e64","0329e2a8a48c06c0c45dfdd2ab33e6455551557d8ebaf8c12fdf7470f8c45f1d28"],"m/2147483647/0/19":["036fe62af85560d7eea7c7af55e60b32a97dca80134d0aedffb19eb2705b9d6e01","02381c2c30b9f81e2a53c69028fbe11803acad0420b267719b7a80870be0baaeb7"],"m/0/0/2":["027bf94b8fc4e9b42683af25fda125ccab8760040717d100270dd4afd032692daf","026382c6c9357250d96dc21e43c053857a64efeac1887fdcbc107fbe3ecfc6115a"],"m/0/0/3":["03fd203acbd9af3cbbfb709458f8952078234a36094f12d00372e4b2b14cfdf419","03f2e5db59aea5dc89f53ac2a9f4ef66d41265c45afc5d763e0ca61ab70c7c61ec"],"m/0/0/4":["02a1d7cf4fcdbbf4de4002b844c3bff1639073f1cd6e5c4a4e02596b45d3f518c2","03b5fba813294e6ae096ea158833453caa5a945609b0a554696091b9b152bb0f7d"],"m/0/0/5":["0261d37e3b56ef4e106c59753037f516a4b1c45e056b2a3e00f8b77f15aaa7f8a5","0256a55e66e0de1603f0d600c0eb5f5486cf3512a776a36f3ab0d1941fc0dc9b09"],"m/0/0/6":["031db2826af215fe6cbe3f6e121b0497840fc49be133cff0a4d4eab679d6b99d70","021dd722c3f35dd04fcdb57f09b76c723d521fb36751de03ffd08096ddf1dc1f86"],"m/0/0/7":["0354ea75bdd9eb5beae7262e4a5eeb58bd10103ee0185e85b749ea39f6615d0f62","03f2c8f3b6478c0501a8578d5caf5ac2974f8213fc5e699d62dd2af58fbe8781d4"],"m/0/0/8":["0282e67df3bcd1e1662469b4c3151fb50ee1e46b75d787d91184c16b9803131f82","02921a7054af1e425f4137a5eb6b34d1f2b9d81c2625230194bc30657bb4277e11"],"m/0/0/9":["033e7e387933983ceab37c8388bd8ebc5119760f493ffe6f083bef0e5dfe22891d","02d660d60cc55d80912e0745cb142a8596a4604fbf72f9aadec0599aa2ed62461a"],"m/0/0/10":["022ce5b2750ae34512199856eab9e912dc25281cd8b88e7688a46c3b9a389701cb","02f14aa1608fce3b6088148709eb5fe72b61699c931fa8d95a45fab1106859d1b0"],"m/0/0/11":["0288dbef3302c1bc5556028adb33e2f9e03c119dbad4f706befb8ce86cea459f2b","03f13ced465e2e0a3aaa8895f3185d5711e0bebdaf507610b7a669ac8fc82da8fe"],"m/0/0/12":["031ab4677885340d2f927ccc9747f4346b79e4eb6c750695095a8a2524610fa94a","038c881910fbd8b50d193db4e0c84f5b7840820397f92cf0718a8e06d027125503"],"m/0/0/13":["031b568452cba22eb7a88c6085489e53e35abd16068882e71a140e47e12dee9c61","020d09885ee362101d12d34ce0918d41593634db1b9413e5415c6755753b9330e8"],"m/0/0/14":["024177bc9aa03cfc72eda2dfddffd7fe9d0c2f007fc3ba1a48280feae2b9fb117a","03394ad321668440c08da76eb35475ba3a8c0e8cbe0ed81468673a8c72d38fe457"],"m/0/0/15":["02037b1cc696ffbe9eba3684edd53653386ef6cd7728401c40120037593a4c2ae2","020ab8d6900ec9c11ca5d96dfc0ce7cf0ee71653a7c45118e89abb4b113147e53a"],"m/0/0/16":["023bcbb8d4726a546087cdb83740adf0ace879b7195a572c652fa8ce4dbe195a04","0392721b230d5163d28b27fc7e059b875711f12b3da448eabe7229bde57530e637"],"m/0/0/17":["02498ee74e849d3e9261dd1863038caf83d6a3bc2eeebecf17055d4bab44dee77f","03d4dc104b2e0981693e8097437de9b05334a85e2c8edb02783897859bdbc93e32"],"m/0/0/18":["0218a9f524fe54abf8c3afd21314296cfd93eaa9227acbd457e6c9a742dc233cf4","03760f3d0c5db969bda698ff9352e3b7c332216c34825f4c6e857e39c9aee7cd35"],"m/0/0/19":["033dd51f7737f0e9db79f5c38e4298bf3396346904ef3933d290a22e5b77048d9e","0221b2eedccb9a37515263071550069b3b349a166f0f131d0028e8600d9a2251b9"],"m/0/0/20":["02cb6c39161f3244d7769f7ab96346cae2cf21cb6f4538f5e7382d363dc2f836c7","034f7bda4d1e9ed6a3774608a4d6cd8582ab59fe3187f8a7a7cf914d89426ebe28"],"m/0/0/21":["035490549d65f1360f10340037250b171470ff4c86966318a2b1eead6d8b969aea","03f6a04f6fcd07a4f32c82d53710ed30e0f54d43d41c67c661d158b3d0830c3ea2"],"m/1/0/2":["02972eae7e4302e319c266578e14a07839c1e788296a92906e6d66d938211dad5f","039ed6b488f1571ad6527acd6b6c5b8453eacf6665dc5cb7852e33d1c8ea73f9fe"],"m/1/0/3":["02bec4728888c2c045108353994bae5731ec7a7b41459023b0023e10b8d616bd30","03ce1efe16214c9eac595382e46a68143dd11a335b3f7c971ddd719ac544a5fc4b"],"m/1/0/4":["030e2df1d341568225d8dfbe5d07e98dae9f90e0f43e19dcc68c998a6ed7bcc1f0","0380f4c07dc84faf42d51779f104aa6e3b5c3ce2d7684b3cb76d49faeefc2b69d6"],"m/1/0/5":["029a54ddaa25f433b493f4b72df8c1d41be2c4d2963b8b61ee63cc86d16c12d066","021567c95e0317442e7367aa4e3378dd46c5bcef5860f789272fea83b917de0669"],"m/1/0/6":["03590320d80b61cc0874b579f467c9b5ccc50d9ef875bcf6bdd12e2d0c211e8973","03ee4677b6ee89a9d355851f2230506c6897ff219062c0df4ad9a85c60f3535f93"],"m/1/0/7":["03caf98ab1c9b79d1dc8029453a6137c08787b04043b79af3cb42d41d2d3f1338f","023f39ae4e2f4f3887d5fc58e0d3a0d7ee267dc04aa257c75b6b2d67d2f5580f81"],"m/1/0/8":["0352a2a3ea8209c9a2b633d788796ac2d16c08022440e04a77ab2835c7f971d266","0291bc248b3da997f35e8fae98a75a91fdac2819d74c4e270899338d48f7389e87"],"m/1/0/9":["02468d32d9c3c62418d506d4cd0da6cd2022d5bcafdb5f847cf7bde7a48ec6848b","032713d90d12eb6a072f3c1db6c0d3b680d3f78883016135fc0f78e8193d41d4b4"],"m/1/0/10":["034863cc6bab9b059be53413ba75c5fc286647c20d7f9e5512ef4754ea301dd1ce","03a33ab9c32a2264ee2464ebbb5892f0e34acf0fdede4f87395a89e9dacdd4930e"],"m/1/0/11":["031e19296695bfe8a96ba3bf58afa805ee1bd5471fddb3929b1678d69d442d69c9","0270feb33956fd9e937019d629523e26437493c0856514011e6aec88baf7721295"],"m/1/0/12":["03cce695d3c3843bf73e851b2446a77d7e235e5b80b4f4474f9946292eb8218742","039ea96c8822f0ec7ed28308d277f3e730480d7573579cd11b89aef4364cd9ffeb"],"m/1/0/13":["02ab4ac38eb405e822d12c0f0f354f04f9ee1d991dde887a5c1171096fe503158f","036809e60cae1203da8884ea1f85d4669ce6e053f8ba605d775e271b70ab4f6787"],"m/1/0/14":["039d61da23a8610fa0ee58eb37d7cea7ea9396c79153da97280ccf5e46718e3bac","03015c27bcc778682781fd6ad30aa6041db0b7e24270818cdceece0043ccc34b26"],"m/1/0/15":["03c088ed669132835d2728b0ecf294271c8388988c6ae264d43ca24f50e4005f81","03e2c118c9445a2ddc4c8afeb0ba49e21be3f818a483d346418b8922b8a371a2b7"],"m/1/0/16":["02bba7df9847f463c6b23eca37a4bd6efa3801a52b8ddfad804d902e783b70c81c","03764b657f23996e31c64a701facc1cbeb0c9edfdd605e2c1ed36cf48197565d45"],"m/1/0/17":["020445179c522295b89bf4bfd582eb03422e3fa20dcd29263925e9f44282d476d8","036e47bdd32f3061aed1c1f8c2a32b038c7b72391cb1f80ebfc150e58f88372766"],"m/1/0/18":["024d88c4bfcbba713d49e1edcd035234aaa1ee76ad7bcf75bf074a16658a6b0b6d","02b861e7a20d89f6875d2e44c78dbadb99503e282e5e60e9f65657af6fea81d425"],"m/1/0/19":["023a8ca9d5300181f157e1930d3b0800eebe7683d8df72e6cbf28834dbf1be5d60","026053c4f84c10d15890c0b254522972931bc2d5b7cdf9c1f9f3137c22edf3ecd3"],"m/1/0/20":["03137c66e9f3d61aba659f408d77a293fa0f3fea4ccb911074a681d6f61a55d023","0291aa1bbfbef59b16b0e37e185a706c589d448cb02e860c5df9c9d7242ecc739f"],"m/1/0/21":["03c08673e0cae55318bc9dcc4b5f11eb3ff71d42de04015e255dde3fd8cba7e09e","02423d4eab06cd5b26e71d145283523c011d58032700c517f00b328d2c90cf109f"]}},"txProposals":{"txps":[{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543144016,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543144645},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543170040},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/0"],"comment":"blablabla","builderObj":{"valueInSat":"29000000","valueOutSat":"8900000","feeSat":"10000","remainderSat":"20090000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":0,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a9010000009300493046022100ccbb8f398f74a76236629b8499ffc6f9518a2091f5a61a9a352c0a10f615961e022100b8f0769c76cf33bec3d7f81d9da2b74cf6e8a5e0a24ee5f48172854d8bcdbfa101475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff02a0cd8700000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac908c32010000000017a914560c292066792531164149c5ed63ad2793a61b928700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543188745,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543189341},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543206819},"rejectedBy":{},"sentTs":1405543207304,"sentTxid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","inputChainPaths":["m/45\'/0/0/0"],"comment":"que parece","builderObj":{"valueInSat":"29000000","valueOutSat":"9000000","feeSat":"10000","remainderSat":"19990000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":1,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a901000000da00483045022035423cc74824ba904907678dda3b62a20a787b96d1b3e9f3e9546f9c57f4e45902210080a1ff1c39f458ac1642b9e948bd62fd70563b5252e749cc8fc642cd763ee830014730440220524a13f36cfb03caa246d7d84de634ec9386f2c39c19bfa926037f48da86262b022050e58a6503d105ad2805f86806810a1aa7f20d6271e1340b42fa91ab6a30f3e801475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff0240548900000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf00531010000000017a9146130a9d51f996b7a1b9d3e10c80930834251909d8700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543505848,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"rejectedBy":{},"sentTs":1405543610315,"sentTxid":"6fe851b54b777a75fe80fa204dc674395e2af69efb1f7c0017e909eb82c3d914","inputChainPaths":["m/45\'/0/1/1"],"comment":"mandaaaaaaa","builderObj":{"valueInSat":"19990000","valueOutSat":"19980000","feeSat":"10000","remainderSat":"0","hashToScriptMap":{"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts":"5221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52ae"},"selectedUtxos":[{"address":"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts","txid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","vout":1,"ts":1405543157,"scriptPubKey":"a9146130a9d51f996b7a1b9d3e10c80930834251909d87","amount":0.1999,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001664f1a8f905e6a6a2ec5f63abd3520840e21541ea8eb4e72272edd9326c99b1601000000db0048304502206b18b3dba2646c552469d8ef52d7656f6a65f563032530f622abdfd8bd4c5cee022100e804b406eddebbc827646141e74dc64c76a770ed4e35183ffd35d265ad9f7d3b01483045022100f6c013638ff0a316b1baa93dfffba6a98cf3033c133e8bd899e933c9c3e47ce10220530f40e7ea52ae58bec695edbec6d566d2ee8e7b5f33f95e33093ad1e29a125401475221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52aeffffffff01e0de3001000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac00000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543781381,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543782017},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381},"rejectedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543794590},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"1","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c000000009200483045022064d877bc5171fbaef909c2a1a924e0023b3ccc0b530cb46653f06ecb230283e8022100bc6658d60ad4f7120d9226c8f6eada87f3b0388f73c458011988bab36e78ba15014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a91421c4a435d9ac263ec55b35a1a5ca95e979639b9b8700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543835343,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543835968},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543850998},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"2","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c0000000092004830450220302baae7de2e0f102bf3af2d5f450f673e51bd143020141a769ccdcdf16af188022100e7abc087c76050ed649e7139a5a136969e74e24a8d8f6223d3219ad033a26451014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a9148b102abba0729fb0690c61cf7187064d692d43d78700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543869803,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543870411},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543890406},"rejectedBy":{},"sentTs":1405543890913,"sentTxid":"6a0f61574ad65e537e7e99298968db565f97b894b61f4c8f8fac8fcaedb83e2b","inputChainPaths":["m/45\'/0/0/1"],"comment":"3","builderObj":{"valueInSat":"29000000","valueOutSat":"1100000","feeSat":"10000","remainderSat":"27890000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c00000000db00483045022100a8ce7907f9fd7dd41dd65c2dec425e008efea06ee7c80787c10c0e210fbf181302207712c0fdd1cb25836ac1fc2fd303c1e26b85e8980417719b9ed50e977a9693ec01483045022100d1780c4f028cd898920aca3eaceba352ed9306cd17f019ae2f634e8facad149a02203c84ab2093da8e22577e93f27a732f0728d4e6db0c749f3cd3d898d6a025152a014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff02e0c81000000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac5091a9010000000017a914cc1cab78458b1a951b91c6dcd7eeeeb682f506388700000000"}}],"walletId":"55d4bd062d32f90a","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPdWUAmaaopPftevC72Jtiu19V8ee5XijL9JvogqfR95uVrL85f8yBdQMq3KyQtG3Q91yWQb3XDbWWpcdWFDAmJ7Xy2XWkGJu","networkName":"testnet","privateKeyCache":{"m/45\'/0/0/0":"b6fd8d1a079efd523da34f31ba81f544fc3d0a728a8a98299d8980682518e79c","m/45\'/0/1/1":"0f4d52d2a99e4c8c1c2edf09fef12407c3abd2304b961198c3f131a8c8443a13","m/45\'/0/0/1":"de5c191c343bd6017b98708c03344849624a14e2c167cfd6eb8dcb075d139293"}},"addressBook":{"msj42CCGruhRsFrGATiUuh25dtxYtnpbTx":{"hidden":false,"createdTs":1405543109222,"copayerId":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","label":"faucet","signature":"3045022067576e5b37f2707a8dc66e57511ad9b10a3125bd95193fff6f8f6402969c3bf3022100adff9f417db07d88face13b3d13f422740d4421440cade1a205684dfdc5d733a"}}}'; From cbc46f534524f16e8c0a03fd3a6a4bb1584c45a2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 29 Sep 2014 11:35:04 -0300 Subject: [PATCH 015/182] obtainNetworkName to wallet --- js/models/Identity.js | 73 +++++++++++++++++++------------------------ js/models/Wallet.js | 14 +++++++++ test/Storage.js | 50 ++++++++++++----------------- test/Wallet.js | 26 +++++++++++++++ test/test.Identity.js | 32 +++---------------- 5 files changed, 98 insertions(+), 97 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 395aba51a..8a80a0fdf 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -182,37 +182,6 @@ Identity.prototype.obtainNetworkName = function(obj) { obj.privateKey.networkName; }; -/** - * @desc Deserialize an object to a Wallet - * @param {Object} wallet object - * @param {string[]} skipFields - fields to skip when importing - * @return {Wallet} - */ -Identity.prototype._fromObj = function(inObj, skipFields) { - var networkName = this.obtainNetworkName(inObj); - preconditions.checkState(networkName); - preconditions.checkArgument(inObj); - - var obj = JSON.parse(JSON.stringify(inObj)); - - // not stored options - obj.opts = obj.opts || {}; - obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay; - - skipFields = skipFields || []; - skipFields.forEach(function(k) { - if (obj[k]) { - delete obj[k]; - } else - throw new Error('unknown field:' + k); - }); - - var w = Wallet.fromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]); - if (!w) return false; - this._checkVersion(w.version); - return w; -}; - /** * @desc Imports a wallet from an encrypted base64 object * @param {string} base64 - the base64 encoded object @@ -222,9 +191,19 @@ Identity.prototype._fromObj = function(inObj, skipFields) { */ Identity.prototype.importWallet = function(base64, passphrase, skipFields) { this.storage.setPassphrase(passphrase); - var walletObj = this.storage.import(base64); - if (!walletObj) return false; - return this.fromObj(walletObj, skipFields); + + var obj = this.storage.decrypt(base64); + if (!obj) return false; + + var w = Wallet.fromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]); + this._checkVersion(w.version); + + this.profile.addWallet(w.id,function(err){ + if (err) return cb(err); + + + w.store(); + }); }; Identity.prototype.migrateWallet = function(walletId, passphrase, cb) { @@ -401,20 +380,34 @@ Identity.prototype.createWallet = function(opts, cb) { this.storage.setPassphrase(opts.passphrase); - var self = this; var w = this._newWallet(opts); - this.profile.addWallet(w.id, function(err) { + this.addWallet(w, function(err) { if (err) return cb(err); - w.store(function(err) { - if (err) return cb(err); - self.profile.setLastOpenedTs(w.id, function(err) { + self.profile.setLastOpenedTs(w.id, function(err) { return cb(err, w); - }); }); }); }; +Identity.prototype.addWallet = function(wallet, cb) { + preconditions.checkArgument(wallet.id); + preconditions.checkArgument(cb); + + var self = this; + self.profile.addWallet(wallet.id, function(err) { + if (err) return cb(err); + + self.wallets.push(w); + + self.store(function(err) { + if (err) return cb(err); + return (err); + }); + }); +}; + + /** * @desc Checks if a version is compatible with the current version * @param {string} inVersion - a version, with major, minor, and revision, period-separated (x.y.z) diff --git a/js/models/Wallet.js b/js/models/Wallet.js index fedd7a4e1..8d501ae26 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -200,6 +200,20 @@ Wallet.delete = function(walletId, storage, cb) { }; +/** + * @desc obtain network name from serialized wallet + * @param {Object} wallet object + * @return {string} network name + */ +Wallet.obtainNetworkName = function(obj) { + return obj.networkName || + (obj.opts ? obj.opts.networkName : null) || + (obj.publicKeyRing ? obj.publicKeyRing.networkName : null) || + obj.privateKey.networkName; +}; + + + /** * @desc Set the copayer id for the owner of this wallet * @param {string} pubkey - the pubkey to set to the {@link Wallet#seededCopayerId} property diff --git a/test/Storage.js b/test/Storage.js index cd5c87104..74b95fb10 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -67,13 +67,13 @@ describe('Storage model', function() { }); }); - describe('#export', function() { - it('should export the encrypted wallet', function(done) { + describe('#encrypt', function() { + it('should encrypt the encrypted wallet', function(done) { s._write(fakeWallet + timeStamp, 'testval', function() { var obj = { test: 'testval' }; - var encrypted = s.export(obj); + var encrypted = s.encrypt(obj); encrypted.length.should.be.greaterThan(10); done(); }); @@ -93,16 +93,6 @@ describe('Storage model', function() { }); }); - describe('#getLastOpened #setLastOpened', function() { - it('should get/set last opened', function() { - s.setLastOpened('hey', function() { - s.getLastOpened(function(v) { - v.should.equal('hey'); - }); - }); - }); - }); - if (is_browser) { describe('#getSessionId', function() { it('should get SessionId', function(done) { @@ -167,7 +157,7 @@ describe('Storage model', function() { }); }); - describe('#getWallets2_Old', function() { + describe.skip('#getWallets2_Old', function() { it('should retrieve wallets from storage', function(done) { var w1 = { name: 'juan', @@ -178,8 +168,8 @@ describe('Storage model', function() { var w2 = { name: 'pepe' }; - s.setFromObj('wallet::1_wallet1', w1, function() { - s.setFromObj('wallet::2', w2, function() { + s.set('wallet::1_wallet1', w1, function() { + s.set('wallet::2', w2, function() { s.getWallets2_Old(function(ws) { ws[0].should.deep.equal({ id: '1', @@ -197,7 +187,7 @@ describe('Storage model', function() { }); - describe('#getWallets', function() { + describe.skip('#getWallets', function() { it('should retrieve wallets from storage both new and old format', function(done) { var w1 = { name: 'juan', @@ -209,8 +199,8 @@ describe('Storage model', function() { name: 'pepe' }; - s.setFromObj('wallet::1_wallet1', w1, function() { - s.setFromObj('wallet::2', w2, function() { + s.set('wallet::1_wallet1', w1, function() { + s.set('wallet::2', w2, function() { s._write('3::name', 'matias', function() { s._write('1::name', 'juan', function() { s.setGlobal('nameFor::3', 'wallet3', function() { @@ -238,7 +228,7 @@ describe('Storage model', function() { }); }); - describe('#deleteWallet_Old', function() { + describe.skip('#deleteWallet_Old', function() { it('should fail to delete a unexisting wallet', function(done) { s._write('1::hola', 'juan', function() { s._write('2::hola', 'juan', function() { @@ -269,7 +259,7 @@ describe('Storage model', function() { }); }); - describe('#deleteWallet', function() { + describe.skip('#deleteWallet', function() { it('should fail to delete a unexisting wallet', function(done) { var w1 = { name: 'juan', @@ -281,8 +271,8 @@ describe('Storage model', function() { name: 'pepe' }; - s.setFromObj('wallet::1', w1, function() { - s.setFromObj('wallet::2', w2, function() { + s.set('wallet::1', w1, function() { + s.set('wallet::2', w2, function() { s.deleteWallet('3', function(err) { err.toString().should.include('WNOTFOUND'); done(); @@ -302,8 +292,8 @@ describe('Storage model', function() { name: 'pepe' }; - s.setFromObj('wallet::1', w1, function() { - s.setFromObj('wallet::2', w2, function() { + s.set('wallet::1', w1, function() { + s.set('wallet::2', w2, function() { s.deleteWallet('1', function(err) { should.not.exist(err); s.getWallets2_Old(function(ws) { @@ -371,9 +361,9 @@ describe('Storage model', function() { }); }); - describe('#setFromObj', function() { + describe('#set', function() { it('should store from an object as single key', function(done) { - s.setFromObj('wallet::id1_nameid1', { + s.set('wallet::id1_nameid1', { 'key': 'val', 'opts': { 'name': 'nameid1' @@ -423,16 +413,16 @@ describe('Storage model', function() { }); }); - describe('#import', function() { + describe('#decrypt', function() { it('should not be able to decrypt with wrong password', function() { s.setPassphrase('xxx'); - var wo = s.import(encryptedLegacy1); + var wo = s.decrypt(encryptedLegacy1); should.not.exist(wo); }); it('should be able to decrypt an old backup', function() { s.setPassphrase(legacyPassword1); - var wo = s.import(encryptedLegacy1); + var wo = s.decrypt(encryptedLegacy1); should.exist(wo); wo.opts.id.should.equal('48ba2f1ffdfe9708'); wo.opts.spendUnconfirmed.should.equal(true); diff --git a/test/Wallet.js b/test/Wallet.js index 400e4c3b1..ca22c96e0 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1793,6 +1793,32 @@ describe('Wallet model', function() { should.exist(n.networkNonce); }); + + describe('#obtainNetworkName', function() { + it('should return the networkname', function() { + Wallet.obtainNetworkName({ + networkName: 'testnet', + }).should.equal('testnet'); + Wallet.obtainNetworkName({ + opts: { + networkName: 'testnet' + } + }).should.equal('testnet'); + Wallet.obtainNetworkName({ + publicKeyRing: { + networkName: 'testnet' + } + }).should.equal('testnet'); + Wallet.obtainNetworkName({ + privateKey: { + networkName: 'testnet' + } + }).should.equal('testnet'); + }); + }); + + + it('should emit notification when tx received', function(done) { var w = cachedCreateW2(); w.blockchain.removeAllListeners = sinon.stub(); diff --git a/test/test.Identity.js b/test/test.Identity.js index db8e6a4b8..8b1a25456 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -172,30 +172,6 @@ describe('Identity model', function() { }); }); - describe('#obtainNetworkName', function() { - it('should return the networkname', function() { - iden.obtainNetworkName({ - networkName: 'testnet', - }).should.equal('testnet'); - iden.obtainNetworkName({ - opts: { - networkName: 'testnet' - } - }).should.equal('testnet'); - iden.obtainNetworkName({ - publicKeyRing: { - networkName: 'testnet' - } - }).should.equal('testnet'); - iden.obtainNetworkName({ - privateKey: { - networkName: 'testnet' - } - }).should.equal('testnet'); - }); - }); - - describe('#createWallet', function() { it('should create wallet', function(done) { iden.createWallet(null, function(err, w) { @@ -249,13 +225,13 @@ describe('Identity model', function() { }); - describe('#import', function() { + describe.only('#importWallet', function() { it('should create wallet from encrypted object', function() { iden.storage.setPassphrase = sinon.spy(); - iden.storage.import = sinon.stub().withArgs('base64').returns('walletObj'); + iden.storage.decrypt = sinon.stub().withArgs('base64').returns('walletObj'); iden.fromObj = sinon.stub().withArgs('walletObj').returns('ok'); - var w = iden.fromEncryptedObj("encrypted object", "123"); + var w = iden.importWallet("encrypted object", "123"); w.should.equal('ok'); iden.storage.setPassphrase.calledOnce.should.be.true; @@ -263,6 +239,8 @@ describe('Identity model', function() { iden.storage.import.calledOnce.should.be.true; iden.fromObj.calledWith('walletObj').should.be.true; }); + + it('should import and update indexes', function() { var wallet = { id: "fake wallet", From 1dd906afcd61f0861a7882f99a26ee53765abf34 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 29 Sep 2014 12:11:10 -0300 Subject: [PATCH 016/182] add sinon for fromObj --- js/models/Identity.js | 66 ++++----- js/models/Storage.js | 327 ++++++++++++++++++++++++++++++++++++++++++ js/models/Wallet.js | 2 +- test/test.Identity.js | 4 +- 4 files changed, 356 insertions(+), 43 deletions(-) create mode 100644 js/models/Storage.js diff --git a/js/models/Identity.js b/js/models/Identity.js index 8a80a0fdf..1ff9f0b78 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -78,6 +78,18 @@ Identity._newStorage = function(opts) { return new Storage(opts); }; +/* for stubbing */ +Identity.prototype._newWallet = function(opts) { + return new Wallet(opts); +}; + +/* for stubbing */ +Identity._walletFromObj = function(o, s, n, b, skip) { + return Wallet.fromObj(o, s, n, b, skip); +}; + + + /** * creates and Identity @@ -169,19 +181,6 @@ Identity.prototype.store = function(opts, cb) { }); }; - -/** - * @desc obtain network name from serialized wallet - * @param {Object} wallet object - * @return {string} network name - */ -Identity.prototype.obtainNetworkName = function(obj) { - return obj.networkName || - (obj.opts ? obj.opts.networkName : null) || - (obj.publicKeyRing ? obj.publicKeyRing.networkName : null) || - obj.privateKey.networkName; -}; - /** * @desc Imports a wallet from an encrypted base64 object * @param {string} base64 - the base64 encoded object @@ -195,14 +194,12 @@ Identity.prototype.importWallet = function(base64, passphrase, skipFields) { var obj = this.storage.decrypt(base64); if (!obj) return false; - var w = Wallet.fromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]); + var networkName = Wallet.obtainNetworkName(obj); + var w = Identity._walletFromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]); this._checkVersion(w.version); - - this.profile.addWallet(w.id,function(err){ + this.addWallet(w.id, function(err) { if (err) return cb(err); - - - w.store(); + w.store(cb); }); }; @@ -296,19 +293,6 @@ Identity.prototype.read_Old = function(walletId, skipFields, cb) { return cb(err, w); }); }; - -/** - * @desc This method instantiates a wallet. Usefull for stubbing. - * - * @param {opts} opts, ready for new Wallet(opts) - * - */ - - -Identity.prototype._newWallet = function(opts) { - return new Wallet(opts); -}; - /** * @desc This method prepares options for a new Wallet * @@ -385,12 +369,13 @@ Identity.prototype.createWallet = function(opts, cb) { this.addWallet(w, function(err) { if (err) return cb(err); self.profile.setLastOpenedTs(w.id, function(err) { - return cb(err, w); + return cb(err, w); }); }); }; Identity.prototype.addWallet = function(wallet, cb) { + preconditions.checkArgument(wallet); preconditions.checkArgument(wallet.id); preconditions.checkArgument(cb); @@ -400,9 +385,8 @@ Identity.prototype.addWallet = function(wallet, cb) { self.wallets.push(w); - self.store(function(err) { - if (err) return cb(err); - return (err); + w.store(function(err) { + return cb(err); }); }); }; @@ -414,10 +398,12 @@ Identity.prototype.addWallet = function(wallet, cb) { * @throws {Error} if there's a major version difference */ Identity.prototype._checkVersion = function(inVersion) { - var thisV = this.version.split('.'); - var thisV0 = parseInt(thisV[0]); - var inV = inVersion.split('.'); - var inV0 = parseInt(inV[0]); + if (inVersion) { + var thisV = this.version.split('.'); + var thisV0 = parseInt(thisV[0]); + var inV = inVersion.split('.'); + var inV0 = parseInt(inV[0]); + } //We only check for major version differences if (thisV0 < inV0) { diff --git a/js/models/Storage.js b/js/models/Storage.js new file mode 100644 index 000000000..0f576722f --- /dev/null +++ b/js/models/Storage.js @@ -0,0 +1,327 @@ +'use strict'; +var preconditions = require('preconditions').singleton(); +var CryptoJS = require('node-cryptojs-aes').CryptoJS; +var bitcore = require('bitcore'); +var preconditions = require('preconditions').instance(); +var _ = require('underscore'); +var CACHE_DURATION = 1000 * 60 * 5; +var id = 0; + + +/* + * Storage wraps db plugin primitives + * with encryption functionalities + * and adds from some extra functionalities + * and a common interfase + */ +function Storage(opts) { + preconditions.checkArgument(opts); + preconditions.checkArgument(opts.password); + opts = opts || {}; + + this.wListCache = {}; + this.__uniqueid = ++id; + this.setPassphrase(opts.password); + + try { + this.db = opts.db || localStorage; + this.sessionStorage = opts.sessionStorage || sessionStorage; + } catch (e) { + console.log('Error in storage:', e); //TODO + }; + + preconditions.checkState(this.db, 'No db defined'); + preconditions.checkState(this.sessionStorage, 'No sessionStorage defined'); +} + +var pps = {}; +Storage.prototype._getPassphrase = function() { + + if (!pps[this.__uniqueid]) + throw new Error('NOPASSPHRASE: No passphrase set'); + + return pps[this.__uniqueid]; +} + +Storage.prototype.setPassphrase = function(password) { + pps[this.__uniqueid] = password; +} + +Storage.prototype._encrypt = function(string) { + var encrypted = CryptoJS.AES.encrypt(string, this._getPassphrase()); + var encryptedBase64 = encrypted.toString(); + return encryptedBase64; +}; + +Storage.prototype._decrypt = function(base64) { + var decryptedStr = null; + try { + var decrypted = CryptoJS.AES.decrypt(base64, this._getPassphrase()); + if (decrypted) + decryptedStr = decrypted.toString(CryptoJS.enc.Utf8); + } catch (e) { + // Error while decrypting + return null; + } + return decryptedStr; +}; + + +Storage.prototype._read = function(k, cb) { + preconditions.checkArgument(cb); + + var self = this; + this.db.getItem(k, function(ret) { + if (!ret) return cb(null); + var ret = self._decrypt(ret); + if (!ret) return cb(null); + + ret = ret.toString(CryptoJS.enc.Utf8); + ret = JSON.parse(ret); + return cb(ret); + }); +}; + +Storage.prototype._write = function(k, v, cb) { + preconditions.checkArgument(cb); + + v = JSON.stringify(v); + v = this._encrypt(v); + this.db.setItem(k, v, cb); +}; + +// get value by key +Storage.prototype.getGlobal = function(k, cb) { + preconditions.checkArgument(cb); + + this.db.getItem(k, function(item) { + cb(item == 'undefined' ? undefined : item); + }); +}; + +// set value for key +Storage.prototype.setGlobal = function(k, v, cb) { + preconditions.checkArgument(cb); + this.db.setItem(k, typeof v === 'object' ? JSON.stringify(v) : v, cb); +}; + +// remove value for key +Storage.prototype.removeGlobal = function(k, cb) { + preconditions.checkArgument(cb); + this.db.removeItem(k, cb); +}; + +Storage.prototype.getSessionId = function(cb) { + preconditions.checkArgument(cb); + var self = this; + + self.sessionStorage.getItem('sessionId', function(sessionId) { + if (sessionId) + return cb(sessionId); + + sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex'); + self.sessionStorage.setItem('sessionId', sessionId, function() { + return cb(sessionId); + }); + }); +}; + +Storage.prototype.setSessionId = function(sessionId, cb) { + this.sessionStorage.setItem('sessionId', sessionId, cb); +}; + +Storage.prototype.get = function(key, cb) { + var self = this; + self._read(key, function(v) { + return cb(null, v); + }) +}; + +Storage.prototype.getFirst = function(prefix, cb) { + var self = this; + this.db.allKeys(function(allKeys) { + var keys = _.filter(allKeys, function(k) { + if ((k === prefix) || k.indexOf(prefix) === 0) return true; + }); + if (keys.length === 0) return cb(new Error('not found')); + self._read(keys[0], function(v) { + if (_.isNull(v)) return cb(new Error('Could not decrypt data')); + return cb(null, v, keys[0]); + }) + }); +}; + +Storage.prototype.set = function(key, obj, cb) { + preconditions.checkArgument(key); + preconditions.checkArgument(cb); + this._write(key, obj, function() { + return cb(); + }); +}; + +Storage.prototype.delete = function(key, cb) { + preconditions.checkArgument(cb); + this.removeGlobal(key, function() { + return cb(); + }); +}; + +Storage.prototype.deletePrefix = function(prefix, cb) { + storage.getFirst(prefix, function(err, v, k) { + if (err && !v) return cb(err); + + storage.delete(k, function(err) { + if (err) return cb(err); + storage.deletePrefix(prefix, cb); + }) + }); +}; + + +Storage.prototype.clearAll = function(cb) { + this.sessionStorage.clear(); + this.db.clear(cb); +}; + +Storage.prototype.decrypt = function(base64) { + var decryptedStr = this._decrypt(base64); + return JSON.parse(decryptedStr); +}; + +Storage.prototype.encrypt = function(obj) { + var string = JSON.stringify(obj); + return this._encrypt(string); +}; + +/* + * OLD functions, only for temporary backwards compatibility + */ + + +Storage.prototype.readWallet_Old = function(walletId, cb) { + var self = this; + this.db.allKeys(function(allKeys) { + var obj = {}; + var keys = _.filter(allKeys, function(k) { + if (k.indexOf(walletId + '::') === 0) return true; + }); + if (keys.length === 0) return cb(new Error('Wallet ' + walletId + ' not found')); + var count = keys.length; + _.each(keys, function(k) { + self._read(k, function(v) { + obj[k.split('::')[1]] = v; + if (--count === 0) return cb(null, obj); + }) + }); + }); +}; + + +Storage.prototype.deleteWallet_Old = function(walletId, cb) { + preconditions.checkArgument(walletId); + preconditions.checkArgument(cb); + var err; + var self = this; + + var toDelete = {}; + + this.db.allKeys(function(allKeys) { + for (var ii in allKeys) { + var key = allKeys[ii]; + var split = key.split('::'); + if (split.length == 2 && split[0] === walletId) { + toDelete[key] = 1; + }; + } + var l = Object.keys(toDelete).length, + j = 0; + if (!l) + return cb(new Error('WNOTFOUND: Wallet not found')); + + toDelete['nameFor::' + walletId] = 1; + l++; + + for (var i in toDelete) { + self.removeGlobal(i, function() { + if (++j == l) + return cb(err); + }); + + } + }); +}; + + +// TODO +Storage.prototype._getWalletIds_Old = function(cb) { + preconditions.checkArgument(cb); + var walletIds = []; + var uniq = {}; + this.db.allKeys(function(keys) { + for (var ii in keys) { + var key = keys[ii]; + var split = key.split('::'); + if (split.length == 2) { + var walletId = split[0]; + + if (!walletId || walletId === 'nameFor' || walletId === 'lock' || walletId === 'wallet') + continue; + + if (typeof uniq[walletId] === 'undefined') { + walletIds.push(walletId); + uniq[walletId] = 1; + } + } + } + return cb(walletIds); + }); +}; + +// TODO +Storage.prototype.getWallets1_Old = function(cb) { + preconditions.checkArgument(cb); + + if (this.wListCache.ts > Date.now()) + return cb(this.wListCache.data) + + var wallets = []; + var self = this; + + this._getWalletIds_Old(function(ids) { + var l = ids.length, + i = 0; + if (!l) + return cb([]); + + _.each(ids, function(id) { + self.getGlobal('nameFor::' + id, function(name) { + wallets.push({ + id: id, + name: name, + }); + if (++i == l) { + self.wListCache.data = wallets; + self.wListCache.ts = Date.now() + CACHE_DURATION; + return cb(wallets); + } + }); + }); + }); +}; + + +Storage.prototype.getWallets_Old = function(cb) { + var self = this; + self.getWallets2_Old(function(wallets) { + self.getWallets1_Old(function(wallets2) { + var ids = _.pluck(wallets, 'id'); + _.each(wallets2, function(w) { + if (!_.contains(ids, w.id)) + wallets.push(w); + }); + return cb(wallets); + }); + }) +}; + +module.exports = Storage; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 8d501ae26..adaf1bc0c 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -209,7 +209,7 @@ Wallet.obtainNetworkName = function(obj) { return obj.networkName || (obj.opts ? obj.opts.networkName : null) || (obj.publicKeyRing ? obj.publicKeyRing.networkName : null) || - obj.privateKey.networkName; + (obj.publicKeyRing ? obj.privateKey.networkName : null); }; diff --git a/test/test.Identity.js b/test/test.Identity.js index 8b1a25456..4f775f5aa 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -228,8 +228,8 @@ describe('Identity model', function() { describe.only('#importWallet', function() { it('should create wallet from encrypted object', function() { iden.storage.setPassphrase = sinon.spy(); - iden.storage.decrypt = sinon.stub().withArgs('base64').returns('walletObj'); - iden.fromObj = sinon.stub().withArgs('walletObj').returns('ok'); + iden.storage.decrypt = sinon.stub().withArgs('base64').returns({networkName:'testnet'}); + Identity._walletFromObj = sinon.stub().withArgs('walletObj').returns('ok'); var w = iden.importWallet("encrypted object", "123"); From 4c582384b0dc1791ee033aa6a5b056c2b7208ea3 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 29 Sep 2014 16:55:45 -0300 Subject: [PATCH 017/182] mv #read to wallet --- js/models/Identity.js | 49 ++-------- js/models/Wallet.js | 52 ++++++++++- test/Wallet.js | 38 ++++++++ test/test.Identity.js | 208 +++++++++++++++++++++--------------------- 4 files changed, 201 insertions(+), 146 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 1ff9f0b78..aa13270db 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -188,7 +188,9 @@ Identity.prototype.store = function(opts, cb) { * @param {string[]} skipFields - fields to ignore when importing * @return {Wallet} */ -Identity.prototype.importWallet = function(base64, passphrase, skipFields) { +Identity.prototype.importWallet = function(base64, passphrase, skipFields, cb) { + preconditions.checkArgument(cb); + this.storage.setPassphrase(passphrase); var obj = this.storage.decrypt(base64); @@ -197,7 +199,7 @@ Identity.prototype.importWallet = function(base64, passphrase, skipFields) { var networkName = Wallet.obtainNetworkName(obj); var w = Identity._walletFromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]); this._checkVersion(w.version); - this.addWallet(w.id, function(err) { + this.addWallet(w, function(err) { if (err) return cb(err); w.store(cb); }); @@ -226,42 +228,6 @@ Identity.prototype.migrateWallet = function(walletId, passphrase, cb) { }; -/** - * @desc Retrieve a wallet from storage - * @param {string} walletId - the wallet id - * @param {string[]} skipFields - parameters to ignore when importing - * @param {function} callback - {err, Wallet} - */ -Identity.prototype._readWallet = function(walletId, skipFields, cb) { - var self = this, - err; - var obj = {}; - - this.storage.getFirst('wallet::' + walletId, function(err, ret) { - if (err) return cb(err); - - _.each(Wallet.PERSISTED_PROPERTIES, function(p) { - obj[p] = ret[p]; - }); - - if (!_.any(_.values(obj))) - return cb(new Error('Wallet not found')); - - var w, err; - obj.id = walletId; - try { - w = self.fromObj(obj, skipFields); - } catch (e) { - if (e && e.message && e.message.indexOf('MISSOPTS')) { - err = new Error('Could not read: ' + walletId); - } else { - err = e; - } - w = null; - } - return cb(err, w); - }); -}; Identity.prototype.read_Old = function(walletId, skipFields, cb) { var self = this, @@ -376,16 +342,15 @@ Identity.prototype.createWallet = function(opts, cb) { Identity.prototype.addWallet = function(wallet, cb) { preconditions.checkArgument(wallet); - preconditions.checkArgument(wallet.id); + preconditions.checkArgument(wallet.getId); preconditions.checkArgument(cb); var self = this; self.profile.addWallet(wallet.id, function(err) { if (err) return cb(err); - self.wallets.push(w); - - w.store(function(err) { + self.wallets.push(wallet); + wallet.store(function(err) { return cb(err); }); }); diff --git a/js/models/Wallet.js b/js/models/Wallet.js index adaf1bc0c..c67d677f0 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -199,6 +199,51 @@ Wallet.delete = function(walletId, storage, cb) { }); }; +/** + * @desc Retrieve a wallet from storage + * + * @param {string} walletId - the wallet id + * @param storage + * @param network + * @param blockchain + * @param {string[]} skipFields - parameters to ignore when importing + * @param {function} callback - {err, Wallet} + * @return {undefined} + */ +Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) { + preconditions.checkArgument(cb); + + var self = this, + err; + var obj = {}; + + storage.getFirst('wallet::' + walletId, function(err, ret) { + if (err) return cb(err); + + if (!ret) + return cb(new Error('WNOTFOUND: Wallet not found')); + + _.each(Wallet.PERSISTED_PROPERTIES, function(p) { + obj[p] = ret[p]; + }); + + var w, err; + obj.id = walletId; + try { + w = self.fromObj(obj, storage, network, blockchain, skipFields); + } catch (e) { + if (e && e.message && e.message.indexOf('MISSOPTS')) { + err = new Error('WERROR: Could not read: ' + walletId); + } else { + err = e; + } + w = null; + } + return cb(err, w); + }); +}; + + /** * @desc obtain network name from serialized wallet @@ -209,7 +254,7 @@ Wallet.obtainNetworkName = function(obj) { return obj.networkName || (obj.opts ? obj.opts.networkName : null) || (obj.publicKeyRing ? obj.publicKeyRing.networkName : null) || - (obj.publicKeyRing ? obj.privateKey.networkName : null); + (obj.privateKey ? obj.privateKey.networkName : null); }; @@ -919,6 +964,11 @@ Wallet.prototype.store = function(cb) { }); }; + +Wallet.prototype.getId = function() { + return this.id; +}; + /** * @desc Serialize the wallet into a plain object. * @return {Object} diff --git a/test/Wallet.js b/test/Wallet.js index ca22c96e0..f16e3ad39 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1887,6 +1887,44 @@ describe('Wallet model', function() { }); }); + describe('#read', function() { + + var s = function() {}; + + var storage = new s(); + var network = new Network(walletConfig.network); + var blockchain = new Blockchain(walletConfig.blockchain); + + + it('should fail to read an unexisting wallet', function(done) { + s.getFirst = sinon.stub().yields(null); + + Wallet.read('123', s, network, blockchain,[], function(err, w) { + err.toString().should.contain('WNOTFOUND'); + done(); + }); + }); + + it('should not read a corrupted wallet', function(done) { + + s.getFirst = sinon.stub().yields(null, '{hola:1}'); + + Wallet.read('123', s, network, blockchain,[], function(err, w) { + err.toString().should.contain('WERROR'); + done(); + }); + }); + + it('should read a wallet', function(done) { + s.getFirst = sinon.stub().yields(null, JSON.parse(o)); + Wallet.read('123', s, network, blockchain,[], function(err, w) { + should.not.exist(err); + done(); + }); + }); + }); + + // DATA var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; diff --git a/test/test.Identity.js b/test/test.Identity.js index 4f775f5aa..cf147e761 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -225,19 +225,114 @@ describe('Identity model', function() { }); - describe.only('#importWallet', function() { - it('should create wallet from encrypted object', function() { + describe.only('#openWallet', function() { + beforeEach(function() { + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); iden.storage.setPassphrase = sinon.spy(); - iden.storage.decrypt = sinon.stub().withArgs('base64').returns({networkName:'testnet'}); - Identity._walletFromObj = sinon.stub().withArgs('walletObj').returns('ok'); + storage.getFirst = sinon.stub().yields('wallet'); + }); - var w = iden.importWallet("encrypted object", "123"); + it('should call setPassphrase', function(done) { - w.should.equal('ok'); - iden.storage.setPassphrase.calledOnce.should.be.true; - iden.storage.setPassphrase.getCall(0).args[0].should.equal('123'); - iden.storage.import.calledOnce.should.be.true; - iden.fromObj.calledWith('walletObj').should.be.true; + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + + iden.openWallet('id123', 'xxx', function(err, w) { + iden.storage.setPassphrase.calledOnce.should.equal(true); + iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + done(); + }); + }); + + it('should call return wallet', function(done) { + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + + iden.openWallet('dummy', 'xxx', function(err, w) { + w.should.equal(s1); + s1.store.calledOnce.should.equal(true); + done(); + }); + }); + + + it('should call #store', function(done) { + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); + + iden.open('dummy', 'xxx', function(err, w) { + s1.store.calledOnce.should.equal(true); + done(); + }); + }); + + it('should call #setLastOpened', function(done) { + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); + + iden.open('dummy', 'xxx', function(err, w) { + iden.storage.setLastOpened.calledOnce.should.equal(true); + iden.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); + done(); + }); + }); + it('should call #migrateWallet', function(done) { + var iden = new Identity(config, '0.0.1'); + iden.storage.setPassphrase = sinon.spy(); + + var s1 = sinon.stub(); + s1.store = sinon.stub().yields(null); + iden.read = sinon.stub().yields(null, s1); + iden.migrateWallet = sinon.stub().yields(null); + iden.storage.deleteWallet_Old = sinon.stub().yields(null); + iden.storage.removeGlobal = sinon.stub().yields(null); + iden.storage.setLastOpened = sinon.stub().yields(null); + + iden.open('dummy', 'xxx', function(err, w) { + iden.migrateWallet.calledOnce.should.equal(true); + iden.migrateWallet.getCall(0).args[0].should.equal('dummy'); + done(); + }); + }); + }); + + + + describe('#importWallet', function() { + it('should create wallet from encrypted object', function(done) { + iden.storage.setPassphrase = sinon.spy(); + iden.storage.decrypt = sinon.stub().withArgs('base64').returns({ + networkName: 'testnet' + }); + + wallet.getId = sinon.stub().returns('ID123'); + Identity._walletFromObj = sinon.stub().returns(wallet); + + iden.importWallet("encrypted object", "123", [], function(err) { + iden.openWallet('ID123', function(err, w) { + should.not.exist(err); + w.should.equal('ok'); + iden.storage.setPassphrase.calledOnce.should.be.true; + iden.storage.setPassphrase.getCall(0).args[0].should.equal('123'); + iden.storage.import.calledOnce.should.be.true; + iden.fromObj.calledWith('walletObj').should.be.true; + }); + }); }); @@ -387,99 +482,6 @@ describe('Identity model', function() { }); - describe('#open', function() { - var opts = { - 'requiredcopayers': 2, - 'totalcopayers': 3 - }; - - it('should call setPassphrase', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - iden.storage.setPassphrase.calledOnce.should.equal(true); - iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); - done(); - }); - }); - - it('should call return wallet', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - w.should.equal(s1); - s1.store.calledOnce.should.equal(true); - done(); - }); - }); - - - it('should call #store', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - s1.store.calledOnce.should.equal(true); - done(); - }); - }); - - it('should call #setLastOpened', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - iden.storage.setLastOpened.calledOnce.should.equal(true); - iden.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); - done(); - }); - }); - it('should call #migrateWallet', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.deleteWallet_Old = sinon.stub().yields(null); - iden.storage.removeGlobal = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - iden.migrateWallet.calledOnce.should.equal(true); - iden.migrateWallet.getCall(0).args[0].should.equal('dummy'); - done(); - }); - }); - }); - describe('#joinWallet', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', From c9a40467599161c1a6b3a5596064350e74274169 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 29 Sep 2014 19:58:00 -0300 Subject: [PATCH 018/182] refactor Profile --- js/models/Identity.js | 105 ++++++++++------------- js/models/Profile.js | 40 ++++----- js/models/Wallet.js | 77 +++++++++++++++-- test/Wallet.js | 1 + test/test.Identity.js | 191 +++++++++--------------------------------- test/test.Profile.js | 31 +++---- 6 files changed, 193 insertions(+), 252 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index aa13270db..8c1571bd0 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -79,7 +79,7 @@ Identity._newStorage = function(opts) { }; /* for stubbing */ -Identity.prototype._newWallet = function(opts) { +Identity._newWallet = function(opts) { return new Wallet(opts); }; @@ -88,6 +88,16 @@ Identity._walletFromObj = function(o, s, n, b, skip) { return Wallet.fromObj(o, s, n, b, skip); }; +/* for stubbing */ +Identity._walletRead = function(id, s, n, b, skip, cb) { + return Wallet.read(id, s, n, b, skip, cb); +}; + +/* for stubbing */ +Identity._walletDelete = function(id, cb) { + return Wallet.delete(id, cb); +}; + @@ -181,6 +191,35 @@ Identity.prototype.store = function(opts, cb) { }); }; +/** + * read + * + * @param opts + * @param cb + * @return {undefined} + */ +Identity.prototype.read = function(opts, cb) { + var self = this; + self.profile.read(opts, function(err) { + if (err) return cb(err); + + var l = self.wallets.length, + i = 0; + if (!l) return cb(); + + _.each(self.wallets, function(w) { + w.store(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 @@ -204,61 +243,6 @@ Identity.prototype.importWallet = function(base64, passphrase, skipFields, cb) { w.store(cb); }); }; - -Identity.prototype.migrateWallet = function(walletId, passphrase, cb) { - var self = this; - - self.storage.setPassphrase(passphrase); - self.read_Old(walletId, null, function(err, wallet) { - if (err) return cb(err); - - wallet.store(function(err) { - if (err) return cb(err); - - self.storage.deleteWallet_Old(walletId, function(err) { - if (err) return cb(err); - - self.storage.removeGlobal('nameFor::' + walletId, function() { - return cb(); - }); - }); - }); - }); - -}; - - - -Identity.prototype.read_Old = function(walletId, skipFields, cb) { - var self = this, - err; - var obj = {}; - - this.storage.readWallet_Old(walletId, function(err, ret) { - if (err) return cb(err); - - _.each(Wallet.PERSISTED_PROPERTIES, function(p) { - obj[p] = ret[p]; - }); - - if (!_.any(_.values(obj))) - return cb(new Error('Wallet not found')); - - var w, err; - obj.id = walletId; - try { - w = self.fromObj(obj, skipFields); - } catch (e) { - if (e && e.message && e.message.indexOf('MISSOPTS')) { - err = new Error('Could not read: ' + walletId); - } else { - err = e; - } - w = null; - } - return cb(err, w); - }); -}; /** * @desc This method prepares options for a new Wallet * @@ -331,7 +315,7 @@ Identity.prototype.createWallet = function(opts, cb) { this.storage.setPassphrase(opts.passphrase); var self = this; - var w = this._newWallet(opts); + var w = Identity._newWallet(opts); this.addWallet(w, function(err) { if (err) return cb(err); self.profile.setLastOpenedTs(w.id, function(err) { @@ -389,10 +373,11 @@ Identity.prototype._checkVersion = function(inVersion) { Identity.prototype.openWallet = function(walletId, passphrase, cb) { preconditions.checkArgument(cb); var self = this; - self.storage.setPassphrase(passphrase); + self.storage.setPassphrase(passphrase); self.migrateWallet(walletId, passphrase, function() { - self._readWallet(walletId, null, function(err, w) { + + Identity._walletRead(walletId, self.storage, self.networks, self.blockchains, [], function(err, w) { if (err) return cb(err); w.store(function(err) { @@ -418,7 +403,7 @@ Identity.prototype.listWallets = function() { Identity.prototype.deleteWallet = function(walletId, cb) { var self = this; - Wallet.delete(walletId, this.storage, function(err) { + Identity._walletDelete(walletId, this.storage, function(err) { if (err) return cb(err); self.profile.deleteWallet(walletId, function(err) { return cb(err); diff --git a/js/models/Profile.js b/js/models/Profile.js index a2a675b30..dd8ee7f0d 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -4,16 +4,18 @@ var _ = require('underscore'); var log = require('../log'); var bitcore = require('bitcore'); -function Profile(info, password, storage) { +function Profile(info, storage) { preconditions.checkArgument(info.email); - preconditions.checkArgument(password); + preconditions.checkArgument(info.hash); preconditions.checkArgument(storage); preconditions.checkArgument(storage.getItem); + this.hash = info.hash; this.email = info.email; this.extra = info.extra; + + this.key = Profile.key(this.hash); this.walletInfos = {}; - this.hash = Profile.hash(this.email, password); this.storage = storage; }; @@ -21,28 +23,26 @@ Profile.hash = function(email, password) { return bitcore.util.sha256ripe160(email + password).toString('hex'); }; -Profile.fromObj = function(obj, password, storage) { - var o = _.clone(obj); - return new Profile(obj, password, storage); +Profile.key = function(hash) { + return 'identity::' + hash; }; +Profile.open = function(email, password, storage, cb) { + preconditions.checkArgument(cb); -Profile.prototype.key = function() { - return 'identity::' + this.hash + '_' + this.email; + var key = Profile.key(Profile.hash(email, password)); + storage.getGlobal(key, function(err, val) { + if (err) return cb(err); + + if (!val) + return cb(new Error('PNOTFOUND: Profile not found')); + + return cb(new Profile(val, storage)); + }); }; Profile.prototype.toObj = function() { - var obj = _.clone(this); - delete obj['hash']; - return JSON.parse(JSON.stringify(obj)); -}; - -Profile.open = function(storage, cb) { - var key = this.key(); - this.storage.getGlobal(key, function(err, val) { - if (!val) return cb(new Error('PNOTFOUND: Profile not found')); - return cb(Profile.fromObj(val, password, storage)); - }); + return JSON.parse(JSON.stringify(this)); }; Profile.prototype.getWallet = function(walletId, cb) { @@ -104,7 +104,7 @@ Profile.prototype.setLasOpenedTs = function(walletId, cb) { Profile.prototype.store = function(opts, cb) { var self = this; var val = self.toObj(); - var key = self.key(); + var key = self.key; self.storage.get(key, function(val2) { if (val2 && !opts.overwrite) { diff --git a/js/models/Wallet.js b/js/models/Wallet.js index c67d677f0..f628e3f6f 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -1009,7 +1009,6 @@ Wallet.prototype.toObj = function() { */ Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { - if (skipFields) { _.each(skipFields, function(k) { if (o[k]) { @@ -1020,19 +1019,23 @@ Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { }); } + var networkName = Wallet.obtainNetworkName(o); + + // TODO Why moving everything to opts. This needs refactoring. - + // // clone opts var opts = JSON.parse(JSON.stringify(o.opts)); opts.addressBook = o.addressBook; opts.settings = o.settings; + if (o.privateKey) { opts.privateKey = PrivateKey.fromObj(o.privateKey); } else { opts.privateKey = new PrivateKey({ - networkName: opts.networkName + networkName: networkName }); } @@ -1040,7 +1043,7 @@ Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing); } else { opts.publicKeyRing = new PublicKeyRing({ - networkName: opts.networkName, + networkName: networkName, requiredCopayers: opts.requiredCopayers, totalCopayers: opts.totalCopayers, }); @@ -1054,15 +1057,15 @@ Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts); } else { opts.txProposals = new TxProposals({ - networkName: this.networkName, + networkName: networkName, }); } opts.lastTimestamp = o.lastTimestamp; opts.storage = storage; - opts.network = network; - opts.blockchain = blockchain; + opts.network = _.isArray(network)? network[networkName] : network; + opts.blockchain = _.isArray(blockchain) ? blockchain[networkName] : blockchain; opts.isImported = true; return new Wallet(opts); @@ -2756,4 +2759,64 @@ Wallet.request = function(options, callback) { return ret; }; +/* + * Old fns, only for compat + * + */ + + +Wallet.prototype.migrateWallet = function(walletId, passphrase, cb) { + var self = this; + + self.storage.setPassphrase(passphrase); + self.read_Old(walletId, null, function(err, wallet) { + if (err) return cb(err); + + wallet.store(function(err) { + if (err) return cb(err); + + self.storage.deleteWallet_Old(walletId, function(err) { + if (err) return cb(err); + + self.storage.removeGlobal('nameFor::' + walletId, function() { + return cb(); + }); + }); + }); + }); + +}; + +Wallet.prototype.read_Old = function(walletId, skipFields, cb) { + var self = this, + err; + var obj = {}; + + this.storage.readWallet_Old(walletId, function(err, ret) { + if (err) return cb(err); + + _.each(Wallet.PERSISTED_PROPERTIES, function(p) { + obj[p] = ret[p]; + }); + + if (!_.any(_.values(obj))) + return cb(new Error('Wallet not found')); + + var w, err; + obj.id = walletId; + try { + w = self.fromObj(obj, skipFields); + } catch (e) { + if (e && e.message && e.message.indexOf('MISSOPTS')) { + err = new Error('Could not read: ' + walletId); + } else { + err = e; + } + w = null; + } + return cb(err, w); + }); +}; + + module.exports = Wallet; diff --git a/test/Wallet.js b/test/Wallet.js index f16e3ad39..4683f7f56 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1809,6 +1809,7 @@ describe('Wallet model', function() { networkName: 'testnet' } }).should.equal('testnet'); + Wallet.obtainNetworkName({ privateKey: { networkName: 'testnet' diff --git a/test/test.Identity.js b/test/test.Identity.js index cf147e761..f8b1ce7ce 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -43,22 +43,20 @@ describe('Identity model', function() { wallet = sinon.stub(); wallet.store = sinon.stub().yields(null); + wallet.getId = sinon.stub().returns('wid:123'); Identity._newWallet = sinon.stub().returns(wallet); profile = sinon.stub(); profile.addWallet = sinon.stub().yields(null);; profile.deleteWallet = sinon.stub().yields(null);; + profile.listWallets = sinon.stub().returns([]); profile.setLastOpenedTs = sinon.stub().yields(null);; profile.store = sinon.stub().yields(null);; Identity._newProfile = sinon.stub().returns(profile); + + iden = new Identity(email, password, config); - - var w = sinon.stub(); - w.store = sinon.stub().yields(null); - iden._newWallet = sinon.stub().returns(w); - - }); @@ -194,7 +192,7 @@ describe('Identity model', function() { iden.createWallet({ privateKeyHex: priv, }, function(err, w) { - iden._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + Identity._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); should.not.exist(err); done(); }); @@ -203,8 +201,8 @@ describe('Identity model', function() { it('should be able to create wallets with random pk', function(done) { iden.createWallet(null, function(err, w1) { iden.createWallet(null, function(err, w2) { - iden._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( - iden._newWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString + Identity._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( + Identity._newWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString ); done(); }); @@ -225,12 +223,18 @@ describe('Identity model', function() { }); - describe.only('#openWallet', function() { + describe('#openWallet', function() { + beforeEach(function() { iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); + iden.profile.setLastOpened = sinon.stub().yields(null); iden.storage.setPassphrase = sinon.spy(); - storage.getFirst = sinon.stub().yields('wallet'); + storage.getFirst = sinon.stub().yields('wallet1234'); + + var wallet = sinon.stub(); + wallet.store = sinon.stub().yields(null); + + Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); }); it('should call setPassphrase', function(done) { @@ -245,67 +249,13 @@ describe('Identity model', function() { }); }); - it('should call return wallet', function(done) { - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); + it('should return wallet and call .store, .setLastOpenedTs & .migrateWallet', function(done) { iden.openWallet('dummy', 'xxx', function(err, w) { - w.should.equal(s1); - s1.store.calledOnce.should.equal(true); - done(); - }); - }); - - - it('should call #store', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - s1.store.calledOnce.should.equal(true); - done(); - }); - }); - - it('should call #setLastOpened', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { - iden.storage.setLastOpened.calledOnce.should.equal(true); - iden.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); - done(); - }); - }); - it('should call #migrateWallet', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - iden.read = sinon.stub().yields(null, s1); - iden.migrateWallet = sinon.stub().yields(null); - iden.storage.deleteWallet_Old = sinon.stub().yields(null); - iden.storage.removeGlobal = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - - iden.open('dummy', 'xxx', function(err, w) { + should.not.exist(err); + w.store.calledOnce.should.equal(true); + iden.profile.setLastOpenedTs.calledOnce.should.equal(true); iden.migrateWallet.calledOnce.should.equal(true); - iden.migrateWallet.getCall(0).args[0].should.equal('dummy'); done(); }); }); @@ -314,6 +264,11 @@ describe('Identity model', function() { describe('#importWallet', function() { + + beforeEach(function() { + iden.migrateWallet = sinon.stub().yields(null); + }); + it('should create wallet from encrypted object', function(done) { iden.storage.setPassphrase = sinon.spy(); iden.storage.decrypt = sinon.stub().withArgs('base64').returns({ @@ -323,14 +278,11 @@ describe('Identity model', function() { wallet.getId = sinon.stub().returns('ID123'); Identity._walletFromObj = sinon.stub().returns(wallet); - iden.importWallet("encrypted object", "123", [], function(err) { - iden.openWallet('ID123', function(err, w) { + iden.importWallet("encrypted object", "xxx", [], function(err) { + iden.openWallet('ID123', 'xxx', function(err, w) { should.not.exist(err); - w.should.equal('ok'); - iden.storage.setPassphrase.calledOnce.should.be.true; - iden.storage.setPassphrase.getCall(0).args[0].should.equal('123'); - iden.storage.import.calledOnce.should.be.true; - iden.fromObj.calledWith('walletObj').should.be.true; + should.exist(w); + done(); }); }); }); @@ -357,84 +309,21 @@ describe('Identity model', function() { }); }); - describe('#getWallets', function() { - it('should return empty array if no wallets', function(done) { - iden.storage.getWallets = sinon.stub().yields([]); - iden.storage.getLastOpened = sinon.stub().yields(null); - - iden.getWallets(function(err, ws) { - should.not.exist(err); - ws.should.deep.equal([]); - done(); - }); - }); - - it('should be able to get current wallets', function(done) { - iden.storage.getWallets = sinon.stub().yields([{ - name: 'w1', - id: 'id1', - }, { - name: 'w', - id: 'id2', - }]); - iden.storage.getLastOpened = sinon.stub().yields(null); - - iden.getWallets(function(err, ws) { - should.not.exist(err); - ws.should.deep.equal([{ - name: 'w1', - id: 'id1', - show: 'w1 ' - }, { - name: 'w', - id: 'id2', - show: 'w ' - }]); - done(); - }); - }); - it('should include last used info', function(done) { - iden.storage.getWallets = sinon.stub().yields([{ - name: 'w1', - id: 'id1', - }, { - name: 'w', - id: 'id2', - }]); - iden.storage.getLastOpened = sinon.stub().yields('id2'); - - iden.getWallets(function(err, ws) { - should.not.exist(err); - ws.should.deep.equal([{ - name: 'w1', - id: 'id1', - show: 'w1 ' - }, { - name: 'w', - id: 'id2', - lastOpened: true, - show: 'w ' - }]); - done(); - }); + describe('#listWallets', function() { + it('should return empty array if no wallets', function() { + iden.listWallets(); + iden.profile.listWallets.calledOnce.should.equal(true); }); }); - describe('#delete', function() { - it('should call deleteWallet', function(done) { - iden.storage.deleteWallet = sinon.stub().yields(null); - iden.delete('xxx', function() { - iden.storage.deleteWallet.getCall(0).args[0].should.equal('xxx'); - done(); - }); - }); - it('should call lastOpened', function(done) { - iden.storage.deleteWallet = sinon.stub().yields(null); - iden.storage.setLastOpened = sinon.stub().yields(null); - iden.delete('xxx', function() { - iden.storage.setLastOpened.calledOnce.should.equal(true); - should.not.exist(iden.storage.setLastOpened.getCall(0).args[0]); + describe('#deleteWallet', function() { + Identity._walletDelete = sinon.stub().callsArgWith(2, null); + + it('should call Profile deleteWallet', function(done) { + iden.profile.deleteWallet = sinon.stub().yields(null); + iden.deleteWallet('xxx', function() { + iden.profile.deleteWallet.getCall(0).args[0].should.equal('xxx'); done(); }); }); diff --git a/test/test.Profile.js b/test/test.Profile.js index b4801aee2..af06fc544 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -11,9 +11,11 @@ var FakeStorage = function() {}; describe('Profile model', function() { var email = 'email@pepe.com'; var password = 'iamnotsatoshi'; + var hash = '1234'; var storage = new FakeStorage(); var opts = { email: email, + hash:hash, }; beforeEach(function() { @@ -34,19 +36,20 @@ describe('Profile model', function() { it('should create an instance', function() { var p = new Profile({ email: email, - }, password, storage); + hash: hash, + }, storage); should.exist(p); }); it('#fromObj #toObj round trip', function() { - var p = new Profile(opts, password, storage); - var p2 = Profile.fromObj(p.toObj(), password, storage); + var p = new Profile(opts, storage); + var p2 = new Profile(p.toObj(), storage); p2.should.deep.equal(p); }); describe('#addWallet', function() { it('should add a wallet id', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.addWallet('123', {}, function(err) { p.getWallet('123').createdTs.should.be.above(123456789); storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); @@ -54,7 +57,7 @@ describe('Profile model', function() { }) }); it('should keep old ts value', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.walletInfos['123'] = { createdTs: 1 }; @@ -66,7 +69,7 @@ describe('Profile model', function() { }) }); it('should add a wallet info', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.addWallet('123', { a: 1, b: 2 @@ -83,14 +86,14 @@ describe('Profile model', function() { describe('#addToWallet', function() { it('should warn if wallet does not exist', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.addToWallet('234',{1:1}, function(err) { err.toString().should.contain('WNOEXIST'); done(); }); }); it('should add info to a wallet', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.addWallet('234', {}, function(err) { p.addToWallet('234',{'hola':1}, function(err) { var w = p.getWallet('234'); @@ -107,7 +110,7 @@ describe('Profile model', function() { describe('#listWallets', function() { it('should list wallets in order', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.addWallet('123', {}, function(err) { p.addWallet('234', {}, function(err) { _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '234']); @@ -119,7 +122,7 @@ describe('Profile model', function() { describe('#deleteWallet', function() { it('should delete a wallet', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.addWallet('123', {}, function(err) { p.addWallet('234', {}, function(err) { p.addWallet('345', {}, function(err) { @@ -133,7 +136,7 @@ describe('Profile model', function() { }); }); it('should warn if wallet does not exist', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.deleteWallet('234', function(err) { err.toString().should.contain('WNOEXIST'); done(); @@ -144,7 +147,7 @@ describe('Profile model', function() { describe('#store', function() { it('should call storage set', function(done) { - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.store({}, function(err) { storage.set.getCall(0).args[1].should.deep.equal(p.toObj()); should.not.exist(err); @@ -153,7 +156,7 @@ describe('Profile model', function() { }); it('should use fail to overwrite', function(done) { storage.get = sinon.stub().yields(123); - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.store({}, function(err) { err.toString().should.contain('PEXISTS'); should.not.exist(storage.set.getCall(0)); @@ -163,7 +166,7 @@ describe('Profile model', function() { it('should use overwrite param', function(done) { storage.get = sinon.stub().yields(123); - var p = new Profile(opts, password, storage); + var p = new Profile(opts, storage); p.store({ overwrite: true }, function(err) { From 3e968129ded4a4bb5616d1d01a8829451e38eb0c Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 08:09:39 -0300 Subject: [PATCH 019/182] migrate wallet compat test to wallet :D :D :D --- js/models/Identity.js | 50 +++++-------------- test/Wallet.js | 57 ++++++++++++++++++++-- test/test.Identity.js | 111 +++--------------------------------------- 3 files changed, 72 insertions(+), 146 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 8c1571bd0..04f00410f 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -55,6 +55,7 @@ function Identity(email, password, opts) { this.walletDefaults = opts.wallet || {}; this.version = opts.version || version; + // open wallets this.wallets = []; this.profile = Identity._newProfile({ email: email, @@ -67,37 +68,34 @@ Identity._newProfile = function(info, password, storage) { return new Profile(info, password, storage); }; -/* for stubbing */ Identity._newInsight = function(opts) { return new Insight(opts); }; - -/* for stubbing */ Identity._newStorage = function(opts) { return new Storage(opts); }; -/* for stubbing */ Identity._newWallet = function(opts) { return new Wallet(opts); }; -/* for stubbing */ Identity._walletFromObj = function(o, s, n, b, skip) { return Wallet.fromObj(o, s, n, b, skip); }; -/* for stubbing */ Identity._walletRead = function(id, s, n, b, skip, cb) { return Wallet.read(id, s, n, b, skip, cb); }; -/* for stubbing */ Identity._walletDelete = function(id, cb) { return Wallet.delete(id, cb); }; +Identity._profileOpen = function(e, p, s, cb) { + return Profile.open(e, p, s, cb); +}; + @@ -145,8 +143,12 @@ Identity.prototype.validate = function(authcode, cb) { */ Identity.open = function(email, password, opts, cb) { var iden = new Identity(email, password, opts); - iden.read(function(err) { - return cb(err, iden); + + Identity._profileOpen(email, password, iden.storage, function(err, profile){ + if (err) return cb(err); + iden.profile = profile; + + return cb(null, iden); }); }; @@ -191,35 +193,6 @@ Identity.prototype.store = function(opts, cb) { }); }; -/** - * read - * - * @param opts - * @param cb - * @return {undefined} - */ -Identity.prototype.read = function(opts, cb) { - var self = this; - self.profile.read(opts, function(err) { - if (err) return cb(err); - - var l = self.wallets.length, - i = 0; - if (!l) return cb(); - - _.each(self.wallets, function(w) { - w.store(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 @@ -324,6 +297,7 @@ Identity.prototype.createWallet = function(opts, cb) { }); }; +// add open wallet? Identity.prototype.addWallet = function(wallet, cb) { preconditions.checkArgument(wallet); preconditions.checkArgument(wallet.getId); diff --git a/test/Wallet.js b/test/Wallet.js index 4683f7f56..740cf83fc 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1891,7 +1891,6 @@ describe('Wallet model', function() { describe('#read', function() { var s = function() {}; - var storage = new s(); var network = new Network(walletConfig.network); var blockchain = new Blockchain(walletConfig.blockchain); @@ -1900,7 +1899,7 @@ describe('Wallet model', function() { it('should fail to read an unexisting wallet', function(done) { s.getFirst = sinon.stub().yields(null); - Wallet.read('123', s, network, blockchain,[], function(err, w) { + Wallet.read('123', s, network, blockchain, [], function(err, w) { err.toString().should.contain('WNOTFOUND'); done(); }); @@ -1910,7 +1909,7 @@ describe('Wallet model', function() { s.getFirst = sinon.stub().yields(null, '{hola:1}'); - Wallet.read('123', s, network, blockchain,[], function(err, w) { + Wallet.read('123', s, network, blockchain, [], function(err, w) { err.toString().should.contain('WERROR'); done(); }); @@ -1918,14 +1917,64 @@ describe('Wallet model', function() { it('should read a wallet', function(done) { s.getFirst = sinon.stub().yields(null, JSON.parse(o)); - Wallet.read('123', s, network, blockchain,[], function(err, w) { + Wallet.read('123', s, network, blockchain, [], function(err, w) { should.not.exist(err); done(); }); }); + + it('should be able to import unencrypted legacy wallet TxProposal: v0', function(done) { + s.getFirst = sinon.stub().yields(null, JSON.parse(legacyO)); + Wallet.read('123', s, network, blockchain, [], function(err, w) { + should.exist(w); + w.id.should.equal('55d4bd062d32f90a'); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + done(); + }); + }); + + it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function(done) { + s.getFirst = sinon.stub().yields(null, JSON.parse(legacy1)); + + Wallet.read('123', s, network, blockchain, [], function(err, w) { + should.exist(w); + w.isReady().should.equal(true); + var wo = w.toObj(); + wo.opts.id.should.equal('48ba2f1ffdfe9708'); + wo.opts.spendUnconfirmed.should.equal(true); + wo.opts.requiredCopayers.should.equal(1); + wo.opts.totalCopayers.should.equal(1); + wo.opts.name.should.equal('pepe wallet'); + wo.opts.version.should.equal('0.4.7'); + wo.publicKeyRing.walletId.should.equal('48ba2f1ffdfe9708'); + wo.publicKeyRing.networkName.should.equal('testnet'); + wo.publicKeyRing.requiredCopayers.should.equal(1); + wo.publicKeyRing.totalCopayers.should.equal(1); + wo.publicKeyRing.indexes.length.should.equal(2); + JSON.stringify(wo.publicKeyRing.indexes[0]).should.equal('{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1}'); + JSON.stringify(wo.publicKeyRing.indexes[1]).should.equal('{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}'); + wo.publicKeyRing.copayersBackup.length.should.equal(1); + wo.publicKeyRing.copayersBackup[0].should.equal('0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f'); + wo.publicKeyRing.copayersExtPubKeys.length.should.equal(1); + wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6'); + wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm'); + wo.privateKey.networkName.should.equal('testnet'); + done(); + }); + }); }); + + var legacyO = '{"opts":{"id":"55d4bd062d32f90a","spendUnconfirmed":true,"requiredCopayers":2,"totalCopayers":2,"name":"xcvzxcv","version":"0.3.2"},"networkNonce":"53d25e8600000009","networkNonces":[],"publicKeyRing":{"walletId":"55d4bd062d32f90a","networkName":"testnet","requiredCopayers":2,"totalCopayers":2,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":4,"receiveIndex":2},{"copayerIndex":1,"changeIndex":5,"receiveIndex":2}],"copayersBackup":["02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5"],"copayersExtPubKeys":["tpubD94LTzAUiW99mpA59nyf6fAHh4xKGmnwbgCV4gU2bRpeN9CRiMSurqme22px5NmJAo6FdcdH883Zu98VbqyhesCJ86kUEjH3Zpufy5FfcaC","tpubDA2U9H6LkRHDRbRxHBp4VTbxPc7JqsvtcLxrE5QJF8z1iT6hMJ1pXSVf57GWRcxXutYvpoXRurDVGsscJauMtnJBkYAWBVExYmm91XQE2zz"],"nicknameFor":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":"asdf","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":"qwerqw"},"publicKeysCache":{"m/0/0/0":["028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90","0332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec8"],"m/1/0/0":["0220ad514cf593d0c3905d3bb49bc5767a9410823bf9b77ea5ef2cf1d1016d77a8","02fd42cf66f1dbdc7bbb9ae09aecea72df479ffe5a0c4641301067e331d12e416d"],"m/1/0/1":["0315f7868eaf1f9b7127e3f7e0222c5e473eea003e34700f4758b6873c525d6723","02a2e8ed5e90dd39e3842fc790e06178997dbca319987f365317589e2a71a93658"],"m/0/1/0":["0244a25a0b97b26707fd855c15b046b901be85a3b70a781d0678608e633440eeca","0358cdcbc528ddfb7173b0dab283f702be82546ff031e4a832a7270080cb875959"],"m/0/1/1":["025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f","020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d"],"m/1/1/0":["02fd0e7c62b7b58d1ea7bb4cb84d53b019df99d3703a42aed73a2cfa15f3af5d08","0355a15912e76072ef50e6643376b8a9da8422ed4f8ea07b1d84d4989be5a39b2e"],"m/1/1/1":["03bc3e1f4db32efd8eb1fd44a1665938d59628429c67e1e8b7054ab5717f4e6750","03c4c817b633ac31f44f16f390af831d35f7d98744a52a0f23e9598967342255f8"],"m/1/1/2":["02826fe7e9da408480ddeb1d4414c5100b350f862ca718e27122681e1a0ca35077","02bd25af907bb3edbf6b2cd1ea90eaa92cc93ec47bea7d339af44c1d2c05708e99"],"m/0/1/2":["0337a1a70364b94745d6e26d2d28919cf528304f52765f12ef43e3d6da0a6c8dc0","039d83db9aa43e6e00e0304e6971b6079d79dc12d8d55ce2e6fc24a52ba8d41329"],"m/0/0/1":["0359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b8138","037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d93"],"m/1/1/3":["02600e5c41670773a213a4cb58c8f2fa3e83840784bc7f0b56925e1075e06632c2","036d01867af5f61371151ef7d9026fa0400a623f6924e404ee0b856625268972f9"],"m/0/1/3":["03e5a9b039b187ca8e065627df402e4a5b196b94198542da7036879de08be63d2d","0304f3e0b70f696d80e5785dc7747d6dcb55ba24c31f2d80bf184b4e582e6b47fc"],"m/1/1/4":["03741afa5bd50d6ba5801064c810fae84f6a4557d6a88ddc8591d0d4eb68a8fc41","0214dd6ce6073b05999fb887098ca6f7e1d0b4fdc0760557786907df353df90d1c"],"m/2147483647/1/0":["033e072a53ea835763a03c66e35c35384736210a1bb7d7ee6d9a3e109e82426b30","02e37b5570c053da8a8ee587be86fc629775c4db890aba2745ccc4e4dcc8c31041"],"m/2147483647/1/1":["0228a6de42ef421c263d1efd9f28d9a7d15a261995028a24eff6b9f1c3fc46e6bf","0226cff885cb0d607cc9cf69a7608316eb3fb2ec344c0c9956246ba776116fc396"],"m/2147483647/1/2":["034fe2a8f0b98445eb5810fe36572ad2f64ed9bf64dc9de624f99c0142cb07c682","02f2c5c758e32293f5c193fd69afadbba83abafb397db01e6f2b447690e900475a"],"m/2147483647/1/3":["02b25ef9434446c51f10678f787e4913de582e34d164bd3b06af7732c5476df1a8","025d51a1efd59bcff22ee2e0af61b21a7ba5f639e20dfdf25690e926005177dd0c"],"m/2147483647/1/4":["03e5734e1d29b2f684d0446b7a2ffbd0ba8952570a502d0d14b1efd8f24b61be53","0258fc28a324848d8d0154e8614815e35c668d274a8f01957bb99aab8dc8f386c0"],"m/2147483647/1/5":["021f9e775246765e1cfba0ae453b4eae6cd4ae5a57a09c319edbe89d4dbbf23be3","02857f66571a1c3eb9e72d22ae88e734c03d448bced4dcfd345c2059468124c741"],"m/2147483647/1/6":["02c072f329391a25255dc6452e5f5220966869dbf736ba8a8c3ae9d273a84bc3fd","030920a8b8e88c4db2871a7df0878a86cf0695f6d96bb50c701c3454f3df25176a"],"m/2147483647/1/7":["036bf329fc19bce10cf1999fae5bfa80290ff7b44776b49c7b0dc9eec6cffcfa21","03955a549875b4f7b9be28b9ff4bcd51ad2bc224430b1634baef890585885d5e1b"],"m/2147483647/1/8":["024879c9c9a261b3141ecfa1c79c4efc25278c844ecd1dcfcb95d9c19581fbdd25","03fb4a5fdb91239df3ccf7f61a5b99e7e72483101e21c9d1ee0d85544e9354c6c7"],"m/2147483647/1/9":["035928a107ec01f78cd586914d5a49710fd42e352b1312e3ad0eeb2c9666fdf8e7","03a54c03093797854829c75357f092356352a109042bbb83bdac20cb4e5eca27ea"],"m/2147483647/1/10":["021e7a3a7efe888c5e820b5cf0f03317b2b4bf438d8563449aeb7a77cade97f136","03ec0960b3d1df52ca3cc2c82b7d97063400da4dd051bba2f9bab6cb44aee01efa"],"m/2147483647/1/11":["035d70c26b7f429861f555f7c0d99947411b23b7f95303fb8d5de5b82a95aa30fc","038b922f7024f5446d6b48e5253643543b35c006d90fd37688105c6cefcd8adb8a"],"m/2147483647/1/12":["02158d6503891c6c65a606221dbf5c68d0832288975914007968419939588ecb24","0248264cb1763a3f4de9b34787b4bc5443ec92ef915927494bb9f1c1c0b498c7ca"],"m/2147483647/1/13":["0349965eea38a25ae0c061faeac4c4e57e648bc4c0f059d07b3b8b7962cbc0dde5","0352243d9269565ce2a1ffdd0b8e43a442c6dd1c9edda86eaaf2cba5a4a95c40f1"],"m/2147483647/1/14":["030fa6e3d0c5cedc0581955395c77cbe134c912a47971023b9695332df3f7bb200","03f2cf09e33326fb59bf3f13e6298d2d5d29c9eae3b872e5a851e8d8d77259c883"],"m/2147483647/1/15":["02bf0d45e41339f552df6f8baf4392142921fd38b0f2a4388a905ff6cbacbc278a","03fabe46bb6706a1b8edfd28c046a8891b4530bbe5305080b72b0d08ebdf7b8c0a"],"m/2147483647/1/16":["03a4e3146ed34d6a8af4e4379e6edcff32cb0373ba232b3d746af3052f674133ac","030311b73c6f5c46ddffc0cfce6e5ed0b671d94267d8e52cd8837f2a479916eb91"],"m/2147483647/1/17":["03233df93c762d2f06c7f5f388e4e0a8dbdb13302acba0d2d6995c487d8aec9f2f","024badfdcb7e772ac7fc1c46d3943b07500edbbece105cdeff3eb9e9fcc9f54782"],"m/2147483647/1/18":["0364035475a098e00eb010c500cad3c90af3e81a4bd613144bc9433a150f14718b","028223dc8142154e7477ce000b3dc13e1d15a901553d9b18864c8645b582b38fe6"],"m/2147483647/1/19":["03971b74b4ac4bdaadf636baa4caa82fe5355471ed6ea05a9cbe5fc6c9e4b9db76","0202ebffacd01f83849e5bc5c0e2c317bc5fb2fbcb2d6d4482a5235f9f1308b61a"],"m/0/1/4":["03005ee9ff028c98fd132e531023f2f2b61ff0d26022f979dd98088d2ba167b031","0345ea82e8dfe38277f0c3aee18d2dd93edb63e8663ac83328a7934d2ca57006f6"],"m/0/1/5":["0391bc4990b71d8a3f156ae7107929ed6372b0b4ba8a868253f71ba7189d1efa02","0312a74cf2e7c0dd41897d04fabfd8cc3187b84a28305cfc79315b24e6fe23a6b4"],"m/0/1/6":["021a38c492607ff9684a4fec445e47b5b7100d3ef9e9dc0d0b37c0a646d28d4f77","03ae0b46ab36f97447ebaa53f2b5c8f090f15395378785f2fd285eeba17fbf3f65"],"m/0/1/7":["0308cdec88c1ffe16edc98853d9c08dbd4ba2541ba566668ca17bda19d7eb3481f","02dd622267c2e68287287b8b61724f76fbe84096a56aa5054af92f8fe25380e2d1"],"m/0/1/8":["039647da9ad725836bcb28a3e0497659a28d7749d1416c421a0a01c62d237ee962","022e22aa61eafda0dd8820427f1a06314d352a15ea8645e7ab9b80920017084d82"],"m/0/1/9":["03a4ade946076c6962b70c70ac7fad3a87efb59a1d0a4e32bda13a6d47fe9df961","029a07235aba04ab69526e117d836d5b3fae5cfc8c5e72b10c6d1afd261ccc19f3"],"m/0/1/10":["03c78e9b6493b22790db1acea20df9444e0f9c424fc5756e7a32c290ae01783953","0254c130ee467a96570c9f5ebea89de04f0b1db1686b164f2694339bef8f25dd88"],"m/0/1/11":["03a762c43318ef8d4840fab04c8db73797dc648825fac60f2730b4c76678df1cf3","0212c684a4de8e750ad2dfe2b136370ab9803eca178ed9a27b3990c29b067de35c"],"m/0/1/12":["02702d221f9b15c5cf75ac2f497a6c63e60213087c3d2d3be46768e3ebd238e26e","03ed58580744deb357258e44548212038670769d8d51e385d4fb8414311fd01b52"],"m/0/1/13":["0320e0597b54c62768352f433389cee4725d6094d7bcb5c72265edcc0933829aff","02c5706f11b9a85f3176c572842b7c9812c2195058d24d945bc026b00312740e76"],"m/0/1/14":["02fe43077676b844226d3aaa62e8a86d237710d92f882366944acbde0c8992fcaf","039a6a8662abb8910741cf331320549665e9feb28ca94d1ab6a43c84fa330b94ee"],"m/0/1/15":["0369f99f72847af93d50ab8ee75b6e7e912d26e27be96f6d6b7215cf7daeff7ba5","02521700cc07c953ba5aa586fb0e4795a34dffc68c5fb43e038be3866e40f4daed"],"m/0/1/16":["02f67d1d89bd8fe2f91c5b973cbdacfb4ba440e7656bce284cf73d549625607347","035da9cfac5a803dcb2b283b02a2515a4a1bcbf3d19e0d180aee8fc30193bc0555"],"m/0/1/17":["02c024ec199d240e8d6c66276b94b91071f7cdf2bef540c29d6d18d25de7b1cf7c","02190865f9dafae3f7f05c093463be5632946422ddda0a6fef6904390792516067"],"m/0/1/18":["035ed504d7704ad984a333b8eb0fceb8be043da9284de31ed84d9e68d90c75507d","033303c415b50421732402df00f4baa219f334647a7eb5014b9f8079864d6ab558"],"m/0/1/19":["02ce49fe86b0eee73663b1ee867b16b97c876af26f12764c528a2e6d0eb55ad3d7","03ab969bc81796b88e44c340d854df955fc60ea17ea92db5d3115595d6dec890d8"],"m/0/1/20":["03e2fa915378cbdffa0d919b0fb50c7256ca731b9d571b3365e486893a1d43079c","038d058b895cf084dccfcc9367e4796a5cf4ddceed6c35f6885d75c80119613350"],"m/0/1/21":["02fcb1bf644446b5b42205272af72f0aeab9e92ca29aafa91c5fb69142764017aa","035c5fe5c8811603279a5b72b6c30735d702817db1eab937c622269e28192ffa90"],"m/0/1/22":["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5","022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"],"m/0/1/23":["03f40b82fe8cacff08879f13c45f443a3dc3ea98e1d75d5f32a19f5e5a8f7a905b","028415ee458e4dcfd440ce969726f3b58ae74fb6cf3995ced099579211e7419844"],"m/1/1/5":["032748a6282e21f571b8c8dd49e775deb83c90fcf88dc4ba81d878536973709c3f","020837cd68f14ce571b335eecd1b6fa0af43e1576dd9721aaca2a8ab639ac6b7cd"],"m/1/1/6":["0337032efb013dc92bb8dccfbdda9f5c28f0039a9c60953d41003d095e9f9778af","03ceed2da6b9603297061dc8eb930112ba726b2ccf5eec67f4866a05ca4049a22b"],"m/1/1/7":["0383c96ac2af7d203f69133b2fab6b68366b5075ad6957fa06759df3b20fbfec70","0311385f79834cedaf2230a48c0f9dc8e794da1869fc595db2518d62debb85579a"],"m/1/1/8":["03efc649680280f4e4df96da923bc88330275004125ebe5483c2f3e05ca52e19a4","02803c02d197d780388259afbd001ae41fa3eb3e2bac9627aff540521c184c3b23"],"m/1/1/9":["03af2fe6aa027a76b42c1c4050a040bfd026ad2daec1bb96a5fe2d026a7df919de","02ce14163047c640228796fb1f72bbe3afb05819ad141598a4f021058a6f79dd3b"],"m/1/1/10":["033770378bd762cf0408e44e4e604bef77e336170428c506949b1a4f1f2963e574","02c58ed43946f699dbd3e36d3e9aab2714cadeb19ecd3a56e4328c50336b4a76cb"],"m/1/1/11":["02898a1545fa19bdca92adc498698d27b86529cd4c08946d9d29604734b86f31af","02b402767a045ede072600924401c0d720000b2ed59fa444bfdbef4a5f1cead745"],"m/1/1/12":["039b8659430be49913e2cd869aa8c99ccf49a13df35837370b792033dadb891483","03264e63df292257cc76babb15d15bef620d1c2f8c3bbc78d6ea02d127e5ee7386"],"m/1/1/13":["02381a559791b8e86bf546e2c718ae63cf24eed0518a58e4d4a4b310adf2cd38fa","02d7f8283a4418d912508901b4a3db0d2103206dfdd74b3c75648671e20ecfd445"],"m/1/1/14":["020376e8c550b7d9faa0b2da947a2a36fab22c6e8190b6f99460b6022017bb97d7","03fbc5299190e6628de28c92aaa12e3a131b21eb7266462c46fbedeb86fa878055"],"m/1/1/15":["027209fd3b0cf7368180a5dbb16b928c997d33fccb78505d48440c7d23eadf5460","03450bfb22858726cd7e228e6733f69457546978a95188565c53e0d1c0d6070ea8"],"m/1/1/16":["03cb355ba04f64293793855121bab5831f84a3a3edf7cd31fccaa6d67c407a4912","028bc897a39c1224610b765a80f4cd8ab79cb37776f58fec9c10ac6f649d1f3c72"],"m/1/1/17":["03f4cb0564d7e2c6b85673503b7954db22779f29a8f3374904573984e318a96bf1","037c11b6ee906d84aa7eed359d758d986d912b6f8e5cbb1acf0982a77b3ef812c4"],"m/1/1/18":["02d2e5798f33f6889472857744316f2d253f25f88379610063f40cfe5798d9858f","0253cefdfe9ca987cbf1c950b6246d5b7a194d8dfad47c3a78dbbc5c1d01511d97"],"m/1/1/19":["0336c325f5aed366ffc10d553f2bfd4d69e66cbe1688d77af14efc8827aea2e318","0378b1b9a6074f9f2ab4fa9ad1e14649c621b0c8124a1b148914d3c10e6ab390c6"],"m/1/1/20":["03ea55740a734689ce778a8c00df8ebf4274c8f66de7d05646fe5c927773ff7f2e","02275b558d49aef955b6dee51a3c0a53f4b076b97bb3f26abcc82540168ec87cac"],"m/1/1/21":["03c77869c9984664eac9c238f4b6d806c9f48ca8a736c48450f398834db2aa915c","02d984f548c7f60c09dad3287cfc48807bc8157123989636c713be61be6a2e9ced"],"m/1/1/22":["03ed7c6a3c854c1f9459891691cc32671402f9e47126919878251e568dbdf353f8","02a113dab22cd9e46967b3fd76b9b9ec1d227d88817a9300e42d332cca2a0877fd"],"m/1/1/23":["02ee186432dcf69fda50a6fdbd94651817d8a271c273a5b70cab3ec4ae77a3753b","02291370aad9de0dac676355ced64e268b0c431a51f42f12d13f5144940fce4285"],"m/1/1/24":["02bf71435e84e66547c8c583d5ba226a5ac4d935e0a9f9603ecd8925c3e847e91a","03578d8657d285a89d9d597632db662cfef9baccfb55c76b1e87948a94fc9de30d"],"m/2147483647/0/0":["02a8425bbe23426219065969f695a6c3e242b24e57226bffdd542be8fd6be968c9","03057a42fdb6569fb1615b173ccb702453db2eac5be4291b82d4511461eafbed87"],"m/2147483647/0/1":["0250c3d3e86e332010c5233c2ec3bc728026002f0037cb3382d6318409b0e70796","02cfac1e7c4c88191201080f8316af52d9faa6ba624a6e160279e9fac4d1cf79a9"],"m/2147483647/0/2":["02a8c266a5b92eb50c8be91f95e4d1ad968b2f57d527377fd642d63fb84474f61a","028cc954ab31bd179ff80b8a05f95430ae534e61b3ff35f5284fa2fbe1832ceccb"],"m/2147483647/0/3":["02f719e1a7ab00ea98611453fb03d44c1da04655bed74af392534d70099039b4c2","03bfa548bfd4718c50bfce173f780eadcfb679d9c0206c91a2fa1879a9cf7558b2"],"m/2147483647/0/4":["0362c0695d397ca26bf47f0e641bb3cfb06ff29ccac2e1d56ded3afcf88b1e688d","02f9d87b05bdb3b9e82f506b43f813041c0e403274adc23d11e5e1651e34b606c2"],"m/2147483647/0/5":["033731323032d4ee08e858fc71f93970444333e183a1d5052e1d08cfb511e262c8","023e12556cef67ade35b7758916b5e1a3ebe074ccd35c5d8eff6b01321f63eb495"],"m/2147483647/0/6":["025d11b90081972bc1c258c9d6f476dfc2f95b69f0e9935322bf9c21deb580ff64","02b065f56a378907354f0738a0ed74f10660c6b5dd68c9f992093b75ce3d7d8b72"],"m/2147483647/0/7":["0210e721e8a35db9d8c855a0d346f60c09208f3be80b39e03af2c29db777332c71","0277f352969fadb1f1835f9a0fa99c6a3c7b6c281be5b2794c88a708eb177ea33d"],"m/2147483647/0/8":["02998d8d41e4215cd2a961a415a3ed0b1f984f1627719a7b102a75864943c4d87b","03d8ed7fc8f68a77f68d3afd007b7aa4c89944195143630ce183f0fa5438f2b559"],"m/2147483647/0/9":["0324fa91737588e4f85937303ce65c3b91b5f2ae506a72d92b83e3f5f9aeeb3c6f","02a011be72c4a400319212228106af278823a97acfe0a67e1ecd866d446b315114"],"m/2147483647/0/10":["025886ba287922a904881c7315e6fcc410a7976741771a5937d3a1a01b529f21fd","0243bb91ceed9d29d0c2ca66a8ab77e82110bbcc023beb4106f787964f44a0b972"],"m/2147483647/0/11":["0369d21684894cc2d4b2f5e581ede3cac9e8db4161a08e7737c1be129bb673d3d5","03c9ef27e3cd3dadc078fdfd9936a7ad9bf7954747085cf8f8a2a5bb3431f68a9f"],"m/2147483647/0/12":["03a73b8fd859bf6acebffdfffa2597199091daedd2c011ac67fc3494d8a1a8ceb6","025a213f7771c8be03f43f2e7f469ad4ef2cf6907ea284b227a786d1f55dfa7144"],"m/2147483647/0/13":["03a09f7ca257e1ab263cd5e6b0addc3ff868b93df132321d98775ca3505efb576f","03454c715739164bc55f347a651439cdf3ec146b35d2927beb60e8290b3916e082"],"m/2147483647/0/14":["03a64b1f7bd94a6b1a6e84ea444e0ba04e9deb86460934ccc37c0615a134a8257b","02794f09210b1811a455f3e1c7bcd35c76dff2523190fef9615eb27e2376acac1c"],"m/2147483647/0/15":["0392dca2fd9a3bc2b2a7d90a848719069fbc5f22bff7327bb8186c032514085263","032ee8a33ea76d70c7ae839448ca6c5b1af89146f2922e23ba1822df42dbc7e66a"],"m/2147483647/0/16":["031a22a1a3c1abad7c4d782ef6ba3cc00f2e8fe549eb33e0732200aff6d3174831","03bdce9781289e0c31cf727f4c93fe46f7930dd8fd68f818ce241f1ede268e8e0e"],"m/2147483647/0/17":["03b12d27e9aea2c2ad598e54e40860a705ac2ca2427aa511b501b38ec368ea5c7d","03e60d35d84d4536cad895215256b312bb4879a8d417251c279995e58f25da3d54"],"m/2147483647/0/18":["0380266cc9a9673676ad6a1b2e7148766df9c25b4dce299e5edc4f65b72aa58e64","0329e2a8a48c06c0c45dfdd2ab33e6455551557d8ebaf8c12fdf7470f8c45f1d28"],"m/2147483647/0/19":["036fe62af85560d7eea7c7af55e60b32a97dca80134d0aedffb19eb2705b9d6e01","02381c2c30b9f81e2a53c69028fbe11803acad0420b267719b7a80870be0baaeb7"],"m/0/0/2":["027bf94b8fc4e9b42683af25fda125ccab8760040717d100270dd4afd032692daf","026382c6c9357250d96dc21e43c053857a64efeac1887fdcbc107fbe3ecfc6115a"],"m/0/0/3":["03fd203acbd9af3cbbfb709458f8952078234a36094f12d00372e4b2b14cfdf419","03f2e5db59aea5dc89f53ac2a9f4ef66d41265c45afc5d763e0ca61ab70c7c61ec"],"m/0/0/4":["02a1d7cf4fcdbbf4de4002b844c3bff1639073f1cd6e5c4a4e02596b45d3f518c2","03b5fba813294e6ae096ea158833453caa5a945609b0a554696091b9b152bb0f7d"],"m/0/0/5":["0261d37e3b56ef4e106c59753037f516a4b1c45e056b2a3e00f8b77f15aaa7f8a5","0256a55e66e0de1603f0d600c0eb5f5486cf3512a776a36f3ab0d1941fc0dc9b09"],"m/0/0/6":["031db2826af215fe6cbe3f6e121b0497840fc49be133cff0a4d4eab679d6b99d70","021dd722c3f35dd04fcdb57f09b76c723d521fb36751de03ffd08096ddf1dc1f86"],"m/0/0/7":["0354ea75bdd9eb5beae7262e4a5eeb58bd10103ee0185e85b749ea39f6615d0f62","03f2c8f3b6478c0501a8578d5caf5ac2974f8213fc5e699d62dd2af58fbe8781d4"],"m/0/0/8":["0282e67df3bcd1e1662469b4c3151fb50ee1e46b75d787d91184c16b9803131f82","02921a7054af1e425f4137a5eb6b34d1f2b9d81c2625230194bc30657bb4277e11"],"m/0/0/9":["033e7e387933983ceab37c8388bd8ebc5119760f493ffe6f083bef0e5dfe22891d","02d660d60cc55d80912e0745cb142a8596a4604fbf72f9aadec0599aa2ed62461a"],"m/0/0/10":["022ce5b2750ae34512199856eab9e912dc25281cd8b88e7688a46c3b9a389701cb","02f14aa1608fce3b6088148709eb5fe72b61699c931fa8d95a45fab1106859d1b0"],"m/0/0/11":["0288dbef3302c1bc5556028adb33e2f9e03c119dbad4f706befb8ce86cea459f2b","03f13ced465e2e0a3aaa8895f3185d5711e0bebdaf507610b7a669ac8fc82da8fe"],"m/0/0/12":["031ab4677885340d2f927ccc9747f4346b79e4eb6c750695095a8a2524610fa94a","038c881910fbd8b50d193db4e0c84f5b7840820397f92cf0718a8e06d027125503"],"m/0/0/13":["031b568452cba22eb7a88c6085489e53e35abd16068882e71a140e47e12dee9c61","020d09885ee362101d12d34ce0918d41593634db1b9413e5415c6755753b9330e8"],"m/0/0/14":["024177bc9aa03cfc72eda2dfddffd7fe9d0c2f007fc3ba1a48280feae2b9fb117a","03394ad321668440c08da76eb35475ba3a8c0e8cbe0ed81468673a8c72d38fe457"],"m/0/0/15":["02037b1cc696ffbe9eba3684edd53653386ef6cd7728401c40120037593a4c2ae2","020ab8d6900ec9c11ca5d96dfc0ce7cf0ee71653a7c45118e89abb4b113147e53a"],"m/0/0/16":["023bcbb8d4726a546087cdb83740adf0ace879b7195a572c652fa8ce4dbe195a04","0392721b230d5163d28b27fc7e059b875711f12b3da448eabe7229bde57530e637"],"m/0/0/17":["02498ee74e849d3e9261dd1863038caf83d6a3bc2eeebecf17055d4bab44dee77f","03d4dc104b2e0981693e8097437de9b05334a85e2c8edb02783897859bdbc93e32"],"m/0/0/18":["0218a9f524fe54abf8c3afd21314296cfd93eaa9227acbd457e6c9a742dc233cf4","03760f3d0c5db969bda698ff9352e3b7c332216c34825f4c6e857e39c9aee7cd35"],"m/0/0/19":["033dd51f7737f0e9db79f5c38e4298bf3396346904ef3933d290a22e5b77048d9e","0221b2eedccb9a37515263071550069b3b349a166f0f131d0028e8600d9a2251b9"],"m/0/0/20":["02cb6c39161f3244d7769f7ab96346cae2cf21cb6f4538f5e7382d363dc2f836c7","034f7bda4d1e9ed6a3774608a4d6cd8582ab59fe3187f8a7a7cf914d89426ebe28"],"m/0/0/21":["035490549d65f1360f10340037250b171470ff4c86966318a2b1eead6d8b969aea","03f6a04f6fcd07a4f32c82d53710ed30e0f54d43d41c67c661d158b3d0830c3ea2"],"m/1/0/2":["02972eae7e4302e319c266578e14a07839c1e788296a92906e6d66d938211dad5f","039ed6b488f1571ad6527acd6b6c5b8453eacf6665dc5cb7852e33d1c8ea73f9fe"],"m/1/0/3":["02bec4728888c2c045108353994bae5731ec7a7b41459023b0023e10b8d616bd30","03ce1efe16214c9eac595382e46a68143dd11a335b3f7c971ddd719ac544a5fc4b"],"m/1/0/4":["030e2df1d341568225d8dfbe5d07e98dae9f90e0f43e19dcc68c998a6ed7bcc1f0","0380f4c07dc84faf42d51779f104aa6e3b5c3ce2d7684b3cb76d49faeefc2b69d6"],"m/1/0/5":["029a54ddaa25f433b493f4b72df8c1d41be2c4d2963b8b61ee63cc86d16c12d066","021567c95e0317442e7367aa4e3378dd46c5bcef5860f789272fea83b917de0669"],"m/1/0/6":["03590320d80b61cc0874b579f467c9b5ccc50d9ef875bcf6bdd12e2d0c211e8973","03ee4677b6ee89a9d355851f2230506c6897ff219062c0df4ad9a85c60f3535f93"],"m/1/0/7":["03caf98ab1c9b79d1dc8029453a6137c08787b04043b79af3cb42d41d2d3f1338f","023f39ae4e2f4f3887d5fc58e0d3a0d7ee267dc04aa257c75b6b2d67d2f5580f81"],"m/1/0/8":["0352a2a3ea8209c9a2b633d788796ac2d16c08022440e04a77ab2835c7f971d266","0291bc248b3da997f35e8fae98a75a91fdac2819d74c4e270899338d48f7389e87"],"m/1/0/9":["02468d32d9c3c62418d506d4cd0da6cd2022d5bcafdb5f847cf7bde7a48ec6848b","032713d90d12eb6a072f3c1db6c0d3b680d3f78883016135fc0f78e8193d41d4b4"],"m/1/0/10":["034863cc6bab9b059be53413ba75c5fc286647c20d7f9e5512ef4754ea301dd1ce","03a33ab9c32a2264ee2464ebbb5892f0e34acf0fdede4f87395a89e9dacdd4930e"],"m/1/0/11":["031e19296695bfe8a96ba3bf58afa805ee1bd5471fddb3929b1678d69d442d69c9","0270feb33956fd9e937019d629523e26437493c0856514011e6aec88baf7721295"],"m/1/0/12":["03cce695d3c3843bf73e851b2446a77d7e235e5b80b4f4474f9946292eb8218742","039ea96c8822f0ec7ed28308d277f3e730480d7573579cd11b89aef4364cd9ffeb"],"m/1/0/13":["02ab4ac38eb405e822d12c0f0f354f04f9ee1d991dde887a5c1171096fe503158f","036809e60cae1203da8884ea1f85d4669ce6e053f8ba605d775e271b70ab4f6787"],"m/1/0/14":["039d61da23a8610fa0ee58eb37d7cea7ea9396c79153da97280ccf5e46718e3bac","03015c27bcc778682781fd6ad30aa6041db0b7e24270818cdceece0043ccc34b26"],"m/1/0/15":["03c088ed669132835d2728b0ecf294271c8388988c6ae264d43ca24f50e4005f81","03e2c118c9445a2ddc4c8afeb0ba49e21be3f818a483d346418b8922b8a371a2b7"],"m/1/0/16":["02bba7df9847f463c6b23eca37a4bd6efa3801a52b8ddfad804d902e783b70c81c","03764b657f23996e31c64a701facc1cbeb0c9edfdd605e2c1ed36cf48197565d45"],"m/1/0/17":["020445179c522295b89bf4bfd582eb03422e3fa20dcd29263925e9f44282d476d8","036e47bdd32f3061aed1c1f8c2a32b038c7b72391cb1f80ebfc150e58f88372766"],"m/1/0/18":["024d88c4bfcbba713d49e1edcd035234aaa1ee76ad7bcf75bf074a16658a6b0b6d","02b861e7a20d89f6875d2e44c78dbadb99503e282e5e60e9f65657af6fea81d425"],"m/1/0/19":["023a8ca9d5300181f157e1930d3b0800eebe7683d8df72e6cbf28834dbf1be5d60","026053c4f84c10d15890c0b254522972931bc2d5b7cdf9c1f9f3137c22edf3ecd3"],"m/1/0/20":["03137c66e9f3d61aba659f408d77a293fa0f3fea4ccb911074a681d6f61a55d023","0291aa1bbfbef59b16b0e37e185a706c589d448cb02e860c5df9c9d7242ecc739f"],"m/1/0/21":["03c08673e0cae55318bc9dcc4b5f11eb3ff71d42de04015e255dde3fd8cba7e09e","02423d4eab06cd5b26e71d145283523c011d58032700c517f00b328d2c90cf109f"]}},"txProposals":{"txps":[{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543144016,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543144645},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543170040},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/0"],"comment":"blablabla","builderObj":{"valueInSat":"29000000","valueOutSat":"8900000","feeSat":"10000","remainderSat":"20090000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":0,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a9010000009300493046022100ccbb8f398f74a76236629b8499ffc6f9518a2091f5a61a9a352c0a10f615961e022100b8f0769c76cf33bec3d7f81d9da2b74cf6e8a5e0a24ee5f48172854d8bcdbfa101475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff02a0cd8700000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac908c32010000000017a914560c292066792531164149c5ed63ad2793a61b928700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543188745,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543189341},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543206819},"rejectedBy":{},"sentTs":1405543207304,"sentTxid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","inputChainPaths":["m/45\'/0/0/0"],"comment":"que parece","builderObj":{"valueInSat":"29000000","valueOutSat":"9000000","feeSat":"10000","remainderSat":"19990000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":1,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a901000000da00483045022035423cc74824ba904907678dda3b62a20a787b96d1b3e9f3e9546f9c57f4e45902210080a1ff1c39f458ac1642b9e948bd62fd70563b5252e749cc8fc642cd763ee830014730440220524a13f36cfb03caa246d7d84de634ec9386f2c39c19bfa926037f48da86262b022050e58a6503d105ad2805f86806810a1aa7f20d6271e1340b42fa91ab6a30f3e801475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff0240548900000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf00531010000000017a9146130a9d51f996b7a1b9d3e10c80930834251909d8700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543505848,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"rejectedBy":{},"sentTs":1405543610315,"sentTxid":"6fe851b54b777a75fe80fa204dc674395e2af69efb1f7c0017e909eb82c3d914","inputChainPaths":["m/45\'/0/1/1"],"comment":"mandaaaaaaa","builderObj":{"valueInSat":"19990000","valueOutSat":"19980000","feeSat":"10000","remainderSat":"0","hashToScriptMap":{"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts":"5221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52ae"},"selectedUtxos":[{"address":"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts","txid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","vout":1,"ts":1405543157,"scriptPubKey":"a9146130a9d51f996b7a1b9d3e10c80930834251909d87","amount":0.1999,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001664f1a8f905e6a6a2ec5f63abd3520840e21541ea8eb4e72272edd9326c99b1601000000db0048304502206b18b3dba2646c552469d8ef52d7656f6a65f563032530f622abdfd8bd4c5cee022100e804b406eddebbc827646141e74dc64c76a770ed4e35183ffd35d265ad9f7d3b01483045022100f6c013638ff0a316b1baa93dfffba6a98cf3033c133e8bd899e933c9c3e47ce10220530f40e7ea52ae58bec695edbec6d566d2ee8e7b5f33f95e33093ad1e29a125401475221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52aeffffffff01e0de3001000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac00000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543781381,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543782017},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381},"rejectedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543794590},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"1","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c000000009200483045022064d877bc5171fbaef909c2a1a924e0023b3ccc0b530cb46653f06ecb230283e8022100bc6658d60ad4f7120d9226c8f6eada87f3b0388f73c458011988bab36e78ba15014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a91421c4a435d9ac263ec55b35a1a5ca95e979639b9b8700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543835343,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543835968},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543850998},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"2","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c0000000092004830450220302baae7de2e0f102bf3af2d5f450f673e51bd143020141a769ccdcdf16af188022100e7abc087c76050ed649e7139a5a136969e74e24a8d8f6223d3219ad033a26451014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a9148b102abba0729fb0690c61cf7187064d692d43d78700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543869803,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543870411},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543890406},"rejectedBy":{},"sentTs":1405543890913,"sentTxid":"6a0f61574ad65e537e7e99298968db565f97b894b61f4c8f8fac8fcaedb83e2b","inputChainPaths":["m/45\'/0/0/1"],"comment":"3","builderObj":{"valueInSat":"29000000","valueOutSat":"1100000","feeSat":"10000","remainderSat":"27890000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c00000000db00483045022100a8ce7907f9fd7dd41dd65c2dec425e008efea06ee7c80787c10c0e210fbf181302207712c0fdd1cb25836ac1fc2fd303c1e26b85e8980417719b9ed50e977a9693ec01483045022100d1780c4f028cd898920aca3eaceba352ed9306cd17f019ae2f634e8facad149a02203c84ab2093da8e22577e93f27a732f0728d4e6db0c749f3cd3d898d6a025152a014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff02e0c81000000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac5091a9010000000017a914cc1cab78458b1a951b91c6dcd7eeeeb682f506388700000000"}}],"walletId":"55d4bd062d32f90a","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPdWUAmaaopPftevC72Jtiu19V8ee5XijL9JvogqfR95uVrL85f8yBdQMq3KyQtG3Q91yWQb3XDbWWpcdWFDAmJ7Xy2XWkGJu","networkName":"testnet","privateKeyCache":{"m/45\'/0/0/0":"b6fd8d1a079efd523da34f31ba81f544fc3d0a728a8a98299d8980682518e79c","m/45\'/0/1/1":"0f4d52d2a99e4c8c1c2edf09fef12407c3abd2304b961198c3f131a8c8443a13","m/45\'/0/0/1":"de5c191c343bd6017b98708c03344849624a14e2c167cfd6eb8dcb075d139293"}},"addressBook":{"msj42CCGruhRsFrGATiUuh25dtxYtnpbTx":{"hidden":false,"createdTs":1405543109222,"copayerId":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","label":"faucet","signature":"3045022067576e5b37f2707a8dc66e57511ad9b10a3125bd95193fff6f8f6402969c3bf3022100adff9f417db07d88face13b3d13f422740d4421440cade1a205684dfdc5d733a"}}}'; + + + + var legacy1 = '{"opts":{"id":"48ba2f1ffdfe9708","spendUnconfirmed":true,"requiredCopayers":1,"totalCopayers":1,"name":"pepe wallet","version":"0.4.7"},"networkNonce":"5405f06b00000001","networkNonces":[],"publicKeyRing":{"walletId":"48ba2f1ffdfe9708","networkName":"testnet","requiredCopayers":1,"totalCopayers":1,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1},{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}],"copayersBackup":["0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f"],"copayersExtPubKeys":["tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"48ba2f1ffdfe9708","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm","networkName":"testnet"},"addressBook":{}}'; + + // DATA var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; diff --git a/test/test.Identity.js b/test/test.Identity.js index f8b1ce7ce..171317ca6 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -130,12 +130,15 @@ describe('Identity model', function() { }); describe('#open', function(done) { - it('should call .read', function(done) { - Identity.prototype.read = sinon.stub().yields(null); + beforeEach(function() { + Identity._profileOpen = sinon.stub().callsArgWith(3, null, 'kk'); + }); + + it('should call ._profileOpen', function(done) { Identity.open(email, password, config, function(err, iden) { should.not.exist(err); - should.exist(iden.profile.addWallet); - iden.read.calledOnce.should.equal(true); + iden.profile.should.equal('kk'); + Identity._profileOpen.calledOnce.should.equal(true); done(); }); }); @@ -329,48 +332,6 @@ describe('Identity model', function() { }); }); - - describe('#read', function() { - it('should fail to read unexisting wallet', function(done) { - iden.storage.getFirst = sinon.stub().yields(null, {}); - - iden.read('id', [], function(err, w) { - should.not.exist(w); - should.exist(err); - should.exist(err.message); - var m = err.message.toString(); - m.should.to.have.string('Wallet not found'); - done(); - }); - }); - it('should fail to read broken wallet', function(done) { - iden.storage.getFirst = sinon.stub().yields(null, { - 'opts': 1 - }); - iden.read('id', [], function(err, w) { - should.not.exist(w); - should.exist(err); - should.exist(err.message); - var m = err.message.toString(); - m.should.to.have.string('Could not read'); - done(); - }); - }); - it('should read existing wallet', function(done) { - var iden = new Identity(config, '0.0.1'); - iden.storage.getFirst = sinon.stub().yields(null, { - 'opts': 1 - }); - iden.fromObj = sinon.stub().returns('ok'); - iden.read('id', [], function(err, w) { - should.not.exist(err); - should.exist(w); - done(); - }); - }); - }); - - describe('#joinWallet', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', @@ -468,7 +429,6 @@ describe('Identity model', function() { }); it('should accept a priv key a input', function() { - var iden = new Identity(config, '0.0.1'); opts.privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd'; var net = iden.networks['testnet']; net.cleanUp = sinon.spy(); @@ -479,7 +439,6 @@ describe('Identity model', function() { }); it('should call network.start with private key', function() { opts.privHex = undefined; - var iden = new Identity(config, '0.0.1'); var net = iden.networks['testnet']; net.cleanUp = sinon.spy(); net.start = sinon.spy(); @@ -490,59 +449,3 @@ describe('Identity model', function() { }); - describe.skip('Backwards compatibility tests', function() { - it('should be able to import unencrypted legacy wallet TxProposal: v0', function() { - var iden = new Identity(config, '0.0.5'); - var w = iden.fromObj(JSON.parse(legacyO)); - - should.exist(w); - w.id.should.equal('55d4bd062d32f90a'); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj()); - should.exist(w.privateKey.toObj()); - }); - - it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function() { - - iden.storage.import = sinon.stub(); - iden.storage.setPassphrase = sinon.spy(); - iden.storage.import.withArgs('dummy').returns(JSON.parse(legacy1)); - - var w = iden.fromEncryptedObj('dummy', 'xxx'); - should.exist(w); - iden.storage.setPassphrase.calledOnce.should.equal(true); - iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); - - w.isReady().should.equal(true); - var wo = w.toObj(); - wo.opts.id.should.equal('48ba2f1ffdfe9708'); - wo.opts.spendUnconfirmed.should.equal(true); - wo.opts.requiredCopayers.should.equal(1); - wo.opts.totalCopayers.should.equal(1); - wo.opts.name.should.equal('pepe wallet'); - wo.opts.version.should.equal('0.4.7'); - wo.publicKeyRing.walletId.should.equal('48ba2f1ffdfe9708'); - wo.publicKeyRing.networkName.should.equal('testnet'); - wo.publicKeyRing.requiredCopayers.should.equal(1); - wo.publicKeyRing.totalCopayers.should.equal(1); - wo.publicKeyRing.indexes.length.should.equal(2); - JSON.stringify(wo.publicKeyRing.indexes[0]).should.equal('{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1}'); - JSON.stringify(wo.publicKeyRing.indexes[1]).should.equal('{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}'); - wo.publicKeyRing.copayersBackup.length.should.equal(1); - wo.publicKeyRing.copayersBackup[0].should.equal('0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f'); - wo.publicKeyRing.copayersExtPubKeys.length.should.equal(1); - wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6'); - wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm'); - wo.privateKey.networkName.should.equal('testnet'); - - }); - }); -}); - - - -var legacyO = '{"opts":{"id":"55d4bd062d32f90a","spendUnconfirmed":true,"requiredCopayers":2,"totalCopayers":2,"name":"xcvzxcv","version":"0.3.2"},"networkNonce":"53d25e8600000009","networkNonces":[],"publicKeyRing":{"walletId":"55d4bd062d32f90a","networkName":"testnet","requiredCopayers":2,"totalCopayers":2,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":4,"receiveIndex":2},{"copayerIndex":1,"changeIndex":5,"receiveIndex":2}],"copayersBackup":["02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5"],"copayersExtPubKeys":["tpubD94LTzAUiW99mpA59nyf6fAHh4xKGmnwbgCV4gU2bRpeN9CRiMSurqme22px5NmJAo6FdcdH883Zu98VbqyhesCJ86kUEjH3Zpufy5FfcaC","tpubDA2U9H6LkRHDRbRxHBp4VTbxPc7JqsvtcLxrE5QJF8z1iT6hMJ1pXSVf57GWRcxXutYvpoXRurDVGsscJauMtnJBkYAWBVExYmm91XQE2zz"],"nicknameFor":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":"asdf","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":"qwerqw"},"publicKeysCache":{"m/0/0/0":["028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90","0332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec8"],"m/1/0/0":["0220ad514cf593d0c3905d3bb49bc5767a9410823bf9b77ea5ef2cf1d1016d77a8","02fd42cf66f1dbdc7bbb9ae09aecea72df479ffe5a0c4641301067e331d12e416d"],"m/1/0/1":["0315f7868eaf1f9b7127e3f7e0222c5e473eea003e34700f4758b6873c525d6723","02a2e8ed5e90dd39e3842fc790e06178997dbca319987f365317589e2a71a93658"],"m/0/1/0":["0244a25a0b97b26707fd855c15b046b901be85a3b70a781d0678608e633440eeca","0358cdcbc528ddfb7173b0dab283f702be82546ff031e4a832a7270080cb875959"],"m/0/1/1":["025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f","020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d"],"m/1/1/0":["02fd0e7c62b7b58d1ea7bb4cb84d53b019df99d3703a42aed73a2cfa15f3af5d08","0355a15912e76072ef50e6643376b8a9da8422ed4f8ea07b1d84d4989be5a39b2e"],"m/1/1/1":["03bc3e1f4db32efd8eb1fd44a1665938d59628429c67e1e8b7054ab5717f4e6750","03c4c817b633ac31f44f16f390af831d35f7d98744a52a0f23e9598967342255f8"],"m/1/1/2":["02826fe7e9da408480ddeb1d4414c5100b350f862ca718e27122681e1a0ca35077","02bd25af907bb3edbf6b2cd1ea90eaa92cc93ec47bea7d339af44c1d2c05708e99"],"m/0/1/2":["0337a1a70364b94745d6e26d2d28919cf528304f52765f12ef43e3d6da0a6c8dc0","039d83db9aa43e6e00e0304e6971b6079d79dc12d8d55ce2e6fc24a52ba8d41329"],"m/0/0/1":["0359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b8138","037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d93"],"m/1/1/3":["02600e5c41670773a213a4cb58c8f2fa3e83840784bc7f0b56925e1075e06632c2","036d01867af5f61371151ef7d9026fa0400a623f6924e404ee0b856625268972f9"],"m/0/1/3":["03e5a9b039b187ca8e065627df402e4a5b196b94198542da7036879de08be63d2d","0304f3e0b70f696d80e5785dc7747d6dcb55ba24c31f2d80bf184b4e582e6b47fc"],"m/1/1/4":["03741afa5bd50d6ba5801064c810fae84f6a4557d6a88ddc8591d0d4eb68a8fc41","0214dd6ce6073b05999fb887098ca6f7e1d0b4fdc0760557786907df353df90d1c"],"m/2147483647/1/0":["033e072a53ea835763a03c66e35c35384736210a1bb7d7ee6d9a3e109e82426b30","02e37b5570c053da8a8ee587be86fc629775c4db890aba2745ccc4e4dcc8c31041"],"m/2147483647/1/1":["0228a6de42ef421c263d1efd9f28d9a7d15a261995028a24eff6b9f1c3fc46e6bf","0226cff885cb0d607cc9cf69a7608316eb3fb2ec344c0c9956246ba776116fc396"],"m/2147483647/1/2":["034fe2a8f0b98445eb5810fe36572ad2f64ed9bf64dc9de624f99c0142cb07c682","02f2c5c758e32293f5c193fd69afadbba83abafb397db01e6f2b447690e900475a"],"m/2147483647/1/3":["02b25ef9434446c51f10678f787e4913de582e34d164bd3b06af7732c5476df1a8","025d51a1efd59bcff22ee2e0af61b21a7ba5f639e20dfdf25690e926005177dd0c"],"m/2147483647/1/4":["03e5734e1d29b2f684d0446b7a2ffbd0ba8952570a502d0d14b1efd8f24b61be53","0258fc28a324848d8d0154e8614815e35c668d274a8f01957bb99aab8dc8f386c0"],"m/2147483647/1/5":["021f9e775246765e1cfba0ae453b4eae6cd4ae5a57a09c319edbe89d4dbbf23be3","02857f66571a1c3eb9e72d22ae88e734c03d448bced4dcfd345c2059468124c741"],"m/2147483647/1/6":["02c072f329391a25255dc6452e5f5220966869dbf736ba8a8c3ae9d273a84bc3fd","030920a8b8e88c4db2871a7df0878a86cf0695f6d96bb50c701c3454f3df25176a"],"m/2147483647/1/7":["036bf329fc19bce10cf1999fae5bfa80290ff7b44776b49c7b0dc9eec6cffcfa21","03955a549875b4f7b9be28b9ff4bcd51ad2bc224430b1634baef890585885d5e1b"],"m/2147483647/1/8":["024879c9c9a261b3141ecfa1c79c4efc25278c844ecd1dcfcb95d9c19581fbdd25","03fb4a5fdb91239df3ccf7f61a5b99e7e72483101e21c9d1ee0d85544e9354c6c7"],"m/2147483647/1/9":["035928a107ec01f78cd586914d5a49710fd42e352b1312e3ad0eeb2c9666fdf8e7","03a54c03093797854829c75357f092356352a109042bbb83bdac20cb4e5eca27ea"],"m/2147483647/1/10":["021e7a3a7efe888c5e820b5cf0f03317b2b4bf438d8563449aeb7a77cade97f136","03ec0960b3d1df52ca3cc2c82b7d97063400da4dd051bba2f9bab6cb44aee01efa"],"m/2147483647/1/11":["035d70c26b7f429861f555f7c0d99947411b23b7f95303fb8d5de5b82a95aa30fc","038b922f7024f5446d6b48e5253643543b35c006d90fd37688105c6cefcd8adb8a"],"m/2147483647/1/12":["02158d6503891c6c65a606221dbf5c68d0832288975914007968419939588ecb24","0248264cb1763a3f4de9b34787b4bc5443ec92ef915927494bb9f1c1c0b498c7ca"],"m/2147483647/1/13":["0349965eea38a25ae0c061faeac4c4e57e648bc4c0f059d07b3b8b7962cbc0dde5","0352243d9269565ce2a1ffdd0b8e43a442c6dd1c9edda86eaaf2cba5a4a95c40f1"],"m/2147483647/1/14":["030fa6e3d0c5cedc0581955395c77cbe134c912a47971023b9695332df3f7bb200","03f2cf09e33326fb59bf3f13e6298d2d5d29c9eae3b872e5a851e8d8d77259c883"],"m/2147483647/1/15":["02bf0d45e41339f552df6f8baf4392142921fd38b0f2a4388a905ff6cbacbc278a","03fabe46bb6706a1b8edfd28c046a8891b4530bbe5305080b72b0d08ebdf7b8c0a"],"m/2147483647/1/16":["03a4e3146ed34d6a8af4e4379e6edcff32cb0373ba232b3d746af3052f674133ac","030311b73c6f5c46ddffc0cfce6e5ed0b671d94267d8e52cd8837f2a479916eb91"],"m/2147483647/1/17":["03233df93c762d2f06c7f5f388e4e0a8dbdb13302acba0d2d6995c487d8aec9f2f","024badfdcb7e772ac7fc1c46d3943b07500edbbece105cdeff3eb9e9fcc9f54782"],"m/2147483647/1/18":["0364035475a098e00eb010c500cad3c90af3e81a4bd613144bc9433a150f14718b","028223dc8142154e7477ce000b3dc13e1d15a901553d9b18864c8645b582b38fe6"],"m/2147483647/1/19":["03971b74b4ac4bdaadf636baa4caa82fe5355471ed6ea05a9cbe5fc6c9e4b9db76","0202ebffacd01f83849e5bc5c0e2c317bc5fb2fbcb2d6d4482a5235f9f1308b61a"],"m/0/1/4":["03005ee9ff028c98fd132e531023f2f2b61ff0d26022f979dd98088d2ba167b031","0345ea82e8dfe38277f0c3aee18d2dd93edb63e8663ac83328a7934d2ca57006f6"],"m/0/1/5":["0391bc4990b71d8a3f156ae7107929ed6372b0b4ba8a868253f71ba7189d1efa02","0312a74cf2e7c0dd41897d04fabfd8cc3187b84a28305cfc79315b24e6fe23a6b4"],"m/0/1/6":["021a38c492607ff9684a4fec445e47b5b7100d3ef9e9dc0d0b37c0a646d28d4f77","03ae0b46ab36f97447ebaa53f2b5c8f090f15395378785f2fd285eeba17fbf3f65"],"m/0/1/7":["0308cdec88c1ffe16edc98853d9c08dbd4ba2541ba566668ca17bda19d7eb3481f","02dd622267c2e68287287b8b61724f76fbe84096a56aa5054af92f8fe25380e2d1"],"m/0/1/8":["039647da9ad725836bcb28a3e0497659a28d7749d1416c421a0a01c62d237ee962","022e22aa61eafda0dd8820427f1a06314d352a15ea8645e7ab9b80920017084d82"],"m/0/1/9":["03a4ade946076c6962b70c70ac7fad3a87efb59a1d0a4e32bda13a6d47fe9df961","029a07235aba04ab69526e117d836d5b3fae5cfc8c5e72b10c6d1afd261ccc19f3"],"m/0/1/10":["03c78e9b6493b22790db1acea20df9444e0f9c424fc5756e7a32c290ae01783953","0254c130ee467a96570c9f5ebea89de04f0b1db1686b164f2694339bef8f25dd88"],"m/0/1/11":["03a762c43318ef8d4840fab04c8db73797dc648825fac60f2730b4c76678df1cf3","0212c684a4de8e750ad2dfe2b136370ab9803eca178ed9a27b3990c29b067de35c"],"m/0/1/12":["02702d221f9b15c5cf75ac2f497a6c63e60213087c3d2d3be46768e3ebd238e26e","03ed58580744deb357258e44548212038670769d8d51e385d4fb8414311fd01b52"],"m/0/1/13":["0320e0597b54c62768352f433389cee4725d6094d7bcb5c72265edcc0933829aff","02c5706f11b9a85f3176c572842b7c9812c2195058d24d945bc026b00312740e76"],"m/0/1/14":["02fe43077676b844226d3aaa62e8a86d237710d92f882366944acbde0c8992fcaf","039a6a8662abb8910741cf331320549665e9feb28ca94d1ab6a43c84fa330b94ee"],"m/0/1/15":["0369f99f72847af93d50ab8ee75b6e7e912d26e27be96f6d6b7215cf7daeff7ba5","02521700cc07c953ba5aa586fb0e4795a34dffc68c5fb43e038be3866e40f4daed"],"m/0/1/16":["02f67d1d89bd8fe2f91c5b973cbdacfb4ba440e7656bce284cf73d549625607347","035da9cfac5a803dcb2b283b02a2515a4a1bcbf3d19e0d180aee8fc30193bc0555"],"m/0/1/17":["02c024ec199d240e8d6c66276b94b91071f7cdf2bef540c29d6d18d25de7b1cf7c","02190865f9dafae3f7f05c093463be5632946422ddda0a6fef6904390792516067"],"m/0/1/18":["035ed504d7704ad984a333b8eb0fceb8be043da9284de31ed84d9e68d90c75507d","033303c415b50421732402df00f4baa219f334647a7eb5014b9f8079864d6ab558"],"m/0/1/19":["02ce49fe86b0eee73663b1ee867b16b97c876af26f12764c528a2e6d0eb55ad3d7","03ab969bc81796b88e44c340d854df955fc60ea17ea92db5d3115595d6dec890d8"],"m/0/1/20":["03e2fa915378cbdffa0d919b0fb50c7256ca731b9d571b3365e486893a1d43079c","038d058b895cf084dccfcc9367e4796a5cf4ddceed6c35f6885d75c80119613350"],"m/0/1/21":["02fcb1bf644446b5b42205272af72f0aeab9e92ca29aafa91c5fb69142764017aa","035c5fe5c8811603279a5b72b6c30735d702817db1eab937c622269e28192ffa90"],"m/0/1/22":["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5","022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"],"m/0/1/23":["03f40b82fe8cacff08879f13c45f443a3dc3ea98e1d75d5f32a19f5e5a8f7a905b","028415ee458e4dcfd440ce969726f3b58ae74fb6cf3995ced099579211e7419844"],"m/1/1/5":["032748a6282e21f571b8c8dd49e775deb83c90fcf88dc4ba81d878536973709c3f","020837cd68f14ce571b335eecd1b6fa0af43e1576dd9721aaca2a8ab639ac6b7cd"],"m/1/1/6":["0337032efb013dc92bb8dccfbdda9f5c28f0039a9c60953d41003d095e9f9778af","03ceed2da6b9603297061dc8eb930112ba726b2ccf5eec67f4866a05ca4049a22b"],"m/1/1/7":["0383c96ac2af7d203f69133b2fab6b68366b5075ad6957fa06759df3b20fbfec70","0311385f79834cedaf2230a48c0f9dc8e794da1869fc595db2518d62debb85579a"],"m/1/1/8":["03efc649680280f4e4df96da923bc88330275004125ebe5483c2f3e05ca52e19a4","02803c02d197d780388259afbd001ae41fa3eb3e2bac9627aff540521c184c3b23"],"m/1/1/9":["03af2fe6aa027a76b42c1c4050a040bfd026ad2daec1bb96a5fe2d026a7df919de","02ce14163047c640228796fb1f72bbe3afb05819ad141598a4f021058a6f79dd3b"],"m/1/1/10":["033770378bd762cf0408e44e4e604bef77e336170428c506949b1a4f1f2963e574","02c58ed43946f699dbd3e36d3e9aab2714cadeb19ecd3a56e4328c50336b4a76cb"],"m/1/1/11":["02898a1545fa19bdca92adc498698d27b86529cd4c08946d9d29604734b86f31af","02b402767a045ede072600924401c0d720000b2ed59fa444bfdbef4a5f1cead745"],"m/1/1/12":["039b8659430be49913e2cd869aa8c99ccf49a13df35837370b792033dadb891483","03264e63df292257cc76babb15d15bef620d1c2f8c3bbc78d6ea02d127e5ee7386"],"m/1/1/13":["02381a559791b8e86bf546e2c718ae63cf24eed0518a58e4d4a4b310adf2cd38fa","02d7f8283a4418d912508901b4a3db0d2103206dfdd74b3c75648671e20ecfd445"],"m/1/1/14":["020376e8c550b7d9faa0b2da947a2a36fab22c6e8190b6f99460b6022017bb97d7","03fbc5299190e6628de28c92aaa12e3a131b21eb7266462c46fbedeb86fa878055"],"m/1/1/15":["027209fd3b0cf7368180a5dbb16b928c997d33fccb78505d48440c7d23eadf5460","03450bfb22858726cd7e228e6733f69457546978a95188565c53e0d1c0d6070ea8"],"m/1/1/16":["03cb355ba04f64293793855121bab5831f84a3a3edf7cd31fccaa6d67c407a4912","028bc897a39c1224610b765a80f4cd8ab79cb37776f58fec9c10ac6f649d1f3c72"],"m/1/1/17":["03f4cb0564d7e2c6b85673503b7954db22779f29a8f3374904573984e318a96bf1","037c11b6ee906d84aa7eed359d758d986d912b6f8e5cbb1acf0982a77b3ef812c4"],"m/1/1/18":["02d2e5798f33f6889472857744316f2d253f25f88379610063f40cfe5798d9858f","0253cefdfe9ca987cbf1c950b6246d5b7a194d8dfad47c3a78dbbc5c1d01511d97"],"m/1/1/19":["0336c325f5aed366ffc10d553f2bfd4d69e66cbe1688d77af14efc8827aea2e318","0378b1b9a6074f9f2ab4fa9ad1e14649c621b0c8124a1b148914d3c10e6ab390c6"],"m/1/1/20":["03ea55740a734689ce778a8c00df8ebf4274c8f66de7d05646fe5c927773ff7f2e","02275b558d49aef955b6dee51a3c0a53f4b076b97bb3f26abcc82540168ec87cac"],"m/1/1/21":["03c77869c9984664eac9c238f4b6d806c9f48ca8a736c48450f398834db2aa915c","02d984f548c7f60c09dad3287cfc48807bc8157123989636c713be61be6a2e9ced"],"m/1/1/22":["03ed7c6a3c854c1f9459891691cc32671402f9e47126919878251e568dbdf353f8","02a113dab22cd9e46967b3fd76b9b9ec1d227d88817a9300e42d332cca2a0877fd"],"m/1/1/23":["02ee186432dcf69fda50a6fdbd94651817d8a271c273a5b70cab3ec4ae77a3753b","02291370aad9de0dac676355ced64e268b0c431a51f42f12d13f5144940fce4285"],"m/1/1/24":["02bf71435e84e66547c8c583d5ba226a5ac4d935e0a9f9603ecd8925c3e847e91a","03578d8657d285a89d9d597632db662cfef9baccfb55c76b1e87948a94fc9de30d"],"m/2147483647/0/0":["02a8425bbe23426219065969f695a6c3e242b24e57226bffdd542be8fd6be968c9","03057a42fdb6569fb1615b173ccb702453db2eac5be4291b82d4511461eafbed87"],"m/2147483647/0/1":["0250c3d3e86e332010c5233c2ec3bc728026002f0037cb3382d6318409b0e70796","02cfac1e7c4c88191201080f8316af52d9faa6ba624a6e160279e9fac4d1cf79a9"],"m/2147483647/0/2":["02a8c266a5b92eb50c8be91f95e4d1ad968b2f57d527377fd642d63fb84474f61a","028cc954ab31bd179ff80b8a05f95430ae534e61b3ff35f5284fa2fbe1832ceccb"],"m/2147483647/0/3":["02f719e1a7ab00ea98611453fb03d44c1da04655bed74af392534d70099039b4c2","03bfa548bfd4718c50bfce173f780eadcfb679d9c0206c91a2fa1879a9cf7558b2"],"m/2147483647/0/4":["0362c0695d397ca26bf47f0e641bb3cfb06ff29ccac2e1d56ded3afcf88b1e688d","02f9d87b05bdb3b9e82f506b43f813041c0e403274adc23d11e5e1651e34b606c2"],"m/2147483647/0/5":["033731323032d4ee08e858fc71f93970444333e183a1d5052e1d08cfb511e262c8","023e12556cef67ade35b7758916b5e1a3ebe074ccd35c5d8eff6b01321f63eb495"],"m/2147483647/0/6":["025d11b90081972bc1c258c9d6f476dfc2f95b69f0e9935322bf9c21deb580ff64","02b065f56a378907354f0738a0ed74f10660c6b5dd68c9f992093b75ce3d7d8b72"],"m/2147483647/0/7":["0210e721e8a35db9d8c855a0d346f60c09208f3be80b39e03af2c29db777332c71","0277f352969fadb1f1835f9a0fa99c6a3c7b6c281be5b2794c88a708eb177ea33d"],"m/2147483647/0/8":["02998d8d41e4215cd2a961a415a3ed0b1f984f1627719a7b102a75864943c4d87b","03d8ed7fc8f68a77f68d3afd007b7aa4c89944195143630ce183f0fa5438f2b559"],"m/2147483647/0/9":["0324fa91737588e4f85937303ce65c3b91b5f2ae506a72d92b83e3f5f9aeeb3c6f","02a011be72c4a400319212228106af278823a97acfe0a67e1ecd866d446b315114"],"m/2147483647/0/10":["025886ba287922a904881c7315e6fcc410a7976741771a5937d3a1a01b529f21fd","0243bb91ceed9d29d0c2ca66a8ab77e82110bbcc023beb4106f787964f44a0b972"],"m/2147483647/0/11":["0369d21684894cc2d4b2f5e581ede3cac9e8db4161a08e7737c1be129bb673d3d5","03c9ef27e3cd3dadc078fdfd9936a7ad9bf7954747085cf8f8a2a5bb3431f68a9f"],"m/2147483647/0/12":["03a73b8fd859bf6acebffdfffa2597199091daedd2c011ac67fc3494d8a1a8ceb6","025a213f7771c8be03f43f2e7f469ad4ef2cf6907ea284b227a786d1f55dfa7144"],"m/2147483647/0/13":["03a09f7ca257e1ab263cd5e6b0addc3ff868b93df132321d98775ca3505efb576f","03454c715739164bc55f347a651439cdf3ec146b35d2927beb60e8290b3916e082"],"m/2147483647/0/14":["03a64b1f7bd94a6b1a6e84ea444e0ba04e9deb86460934ccc37c0615a134a8257b","02794f09210b1811a455f3e1c7bcd35c76dff2523190fef9615eb27e2376acac1c"],"m/2147483647/0/15":["0392dca2fd9a3bc2b2a7d90a848719069fbc5f22bff7327bb8186c032514085263","032ee8a33ea76d70c7ae839448ca6c5b1af89146f2922e23ba1822df42dbc7e66a"],"m/2147483647/0/16":["031a22a1a3c1abad7c4d782ef6ba3cc00f2e8fe549eb33e0732200aff6d3174831","03bdce9781289e0c31cf727f4c93fe46f7930dd8fd68f818ce241f1ede268e8e0e"],"m/2147483647/0/17":["03b12d27e9aea2c2ad598e54e40860a705ac2ca2427aa511b501b38ec368ea5c7d","03e60d35d84d4536cad895215256b312bb4879a8d417251c279995e58f25da3d54"],"m/2147483647/0/18":["0380266cc9a9673676ad6a1b2e7148766df9c25b4dce299e5edc4f65b72aa58e64","0329e2a8a48c06c0c45dfdd2ab33e6455551557d8ebaf8c12fdf7470f8c45f1d28"],"m/2147483647/0/19":["036fe62af85560d7eea7c7af55e60b32a97dca80134d0aedffb19eb2705b9d6e01","02381c2c30b9f81e2a53c69028fbe11803acad0420b267719b7a80870be0baaeb7"],"m/0/0/2":["027bf94b8fc4e9b42683af25fda125ccab8760040717d100270dd4afd032692daf","026382c6c9357250d96dc21e43c053857a64efeac1887fdcbc107fbe3ecfc6115a"],"m/0/0/3":["03fd203acbd9af3cbbfb709458f8952078234a36094f12d00372e4b2b14cfdf419","03f2e5db59aea5dc89f53ac2a9f4ef66d41265c45afc5d763e0ca61ab70c7c61ec"],"m/0/0/4":["02a1d7cf4fcdbbf4de4002b844c3bff1639073f1cd6e5c4a4e02596b45d3f518c2","03b5fba813294e6ae096ea158833453caa5a945609b0a554696091b9b152bb0f7d"],"m/0/0/5":["0261d37e3b56ef4e106c59753037f516a4b1c45e056b2a3e00f8b77f15aaa7f8a5","0256a55e66e0de1603f0d600c0eb5f5486cf3512a776a36f3ab0d1941fc0dc9b09"],"m/0/0/6":["031db2826af215fe6cbe3f6e121b0497840fc49be133cff0a4d4eab679d6b99d70","021dd722c3f35dd04fcdb57f09b76c723d521fb36751de03ffd08096ddf1dc1f86"],"m/0/0/7":["0354ea75bdd9eb5beae7262e4a5eeb58bd10103ee0185e85b749ea39f6615d0f62","03f2c8f3b6478c0501a8578d5caf5ac2974f8213fc5e699d62dd2af58fbe8781d4"],"m/0/0/8":["0282e67df3bcd1e1662469b4c3151fb50ee1e46b75d787d91184c16b9803131f82","02921a7054af1e425f4137a5eb6b34d1f2b9d81c2625230194bc30657bb4277e11"],"m/0/0/9":["033e7e387933983ceab37c8388bd8ebc5119760f493ffe6f083bef0e5dfe22891d","02d660d60cc55d80912e0745cb142a8596a4604fbf72f9aadec0599aa2ed62461a"],"m/0/0/10":["022ce5b2750ae34512199856eab9e912dc25281cd8b88e7688a46c3b9a389701cb","02f14aa1608fce3b6088148709eb5fe72b61699c931fa8d95a45fab1106859d1b0"],"m/0/0/11":["0288dbef3302c1bc5556028adb33e2f9e03c119dbad4f706befb8ce86cea459f2b","03f13ced465e2e0a3aaa8895f3185d5711e0bebdaf507610b7a669ac8fc82da8fe"],"m/0/0/12":["031ab4677885340d2f927ccc9747f4346b79e4eb6c750695095a8a2524610fa94a","038c881910fbd8b50d193db4e0c84f5b7840820397f92cf0718a8e06d027125503"],"m/0/0/13":["031b568452cba22eb7a88c6085489e53e35abd16068882e71a140e47e12dee9c61","020d09885ee362101d12d34ce0918d41593634db1b9413e5415c6755753b9330e8"],"m/0/0/14":["024177bc9aa03cfc72eda2dfddffd7fe9d0c2f007fc3ba1a48280feae2b9fb117a","03394ad321668440c08da76eb35475ba3a8c0e8cbe0ed81468673a8c72d38fe457"],"m/0/0/15":["02037b1cc696ffbe9eba3684edd53653386ef6cd7728401c40120037593a4c2ae2","020ab8d6900ec9c11ca5d96dfc0ce7cf0ee71653a7c45118e89abb4b113147e53a"],"m/0/0/16":["023bcbb8d4726a546087cdb83740adf0ace879b7195a572c652fa8ce4dbe195a04","0392721b230d5163d28b27fc7e059b875711f12b3da448eabe7229bde57530e637"],"m/0/0/17":["02498ee74e849d3e9261dd1863038caf83d6a3bc2eeebecf17055d4bab44dee77f","03d4dc104b2e0981693e8097437de9b05334a85e2c8edb02783897859bdbc93e32"],"m/0/0/18":["0218a9f524fe54abf8c3afd21314296cfd93eaa9227acbd457e6c9a742dc233cf4","03760f3d0c5db969bda698ff9352e3b7c332216c34825f4c6e857e39c9aee7cd35"],"m/0/0/19":["033dd51f7737f0e9db79f5c38e4298bf3396346904ef3933d290a22e5b77048d9e","0221b2eedccb9a37515263071550069b3b349a166f0f131d0028e8600d9a2251b9"],"m/0/0/20":["02cb6c39161f3244d7769f7ab96346cae2cf21cb6f4538f5e7382d363dc2f836c7","034f7bda4d1e9ed6a3774608a4d6cd8582ab59fe3187f8a7a7cf914d89426ebe28"],"m/0/0/21":["035490549d65f1360f10340037250b171470ff4c86966318a2b1eead6d8b969aea","03f6a04f6fcd07a4f32c82d53710ed30e0f54d43d41c67c661d158b3d0830c3ea2"],"m/1/0/2":["02972eae7e4302e319c266578e14a07839c1e788296a92906e6d66d938211dad5f","039ed6b488f1571ad6527acd6b6c5b8453eacf6665dc5cb7852e33d1c8ea73f9fe"],"m/1/0/3":["02bec4728888c2c045108353994bae5731ec7a7b41459023b0023e10b8d616bd30","03ce1efe16214c9eac595382e46a68143dd11a335b3f7c971ddd719ac544a5fc4b"],"m/1/0/4":["030e2df1d341568225d8dfbe5d07e98dae9f90e0f43e19dcc68c998a6ed7bcc1f0","0380f4c07dc84faf42d51779f104aa6e3b5c3ce2d7684b3cb76d49faeefc2b69d6"],"m/1/0/5":["029a54ddaa25f433b493f4b72df8c1d41be2c4d2963b8b61ee63cc86d16c12d066","021567c95e0317442e7367aa4e3378dd46c5bcef5860f789272fea83b917de0669"],"m/1/0/6":["03590320d80b61cc0874b579f467c9b5ccc50d9ef875bcf6bdd12e2d0c211e8973","03ee4677b6ee89a9d355851f2230506c6897ff219062c0df4ad9a85c60f3535f93"],"m/1/0/7":["03caf98ab1c9b79d1dc8029453a6137c08787b04043b79af3cb42d41d2d3f1338f","023f39ae4e2f4f3887d5fc58e0d3a0d7ee267dc04aa257c75b6b2d67d2f5580f81"],"m/1/0/8":["0352a2a3ea8209c9a2b633d788796ac2d16c08022440e04a77ab2835c7f971d266","0291bc248b3da997f35e8fae98a75a91fdac2819d74c4e270899338d48f7389e87"],"m/1/0/9":["02468d32d9c3c62418d506d4cd0da6cd2022d5bcafdb5f847cf7bde7a48ec6848b","032713d90d12eb6a072f3c1db6c0d3b680d3f78883016135fc0f78e8193d41d4b4"],"m/1/0/10":["034863cc6bab9b059be53413ba75c5fc286647c20d7f9e5512ef4754ea301dd1ce","03a33ab9c32a2264ee2464ebbb5892f0e34acf0fdede4f87395a89e9dacdd4930e"],"m/1/0/11":["031e19296695bfe8a96ba3bf58afa805ee1bd5471fddb3929b1678d69d442d69c9","0270feb33956fd9e937019d629523e26437493c0856514011e6aec88baf7721295"],"m/1/0/12":["03cce695d3c3843bf73e851b2446a77d7e235e5b80b4f4474f9946292eb8218742","039ea96c8822f0ec7ed28308d277f3e730480d7573579cd11b89aef4364cd9ffeb"],"m/1/0/13":["02ab4ac38eb405e822d12c0f0f354f04f9ee1d991dde887a5c1171096fe503158f","036809e60cae1203da8884ea1f85d4669ce6e053f8ba605d775e271b70ab4f6787"],"m/1/0/14":["039d61da23a8610fa0ee58eb37d7cea7ea9396c79153da97280ccf5e46718e3bac","03015c27bcc778682781fd6ad30aa6041db0b7e24270818cdceece0043ccc34b26"],"m/1/0/15":["03c088ed669132835d2728b0ecf294271c8388988c6ae264d43ca24f50e4005f81","03e2c118c9445a2ddc4c8afeb0ba49e21be3f818a483d346418b8922b8a371a2b7"],"m/1/0/16":["02bba7df9847f463c6b23eca37a4bd6efa3801a52b8ddfad804d902e783b70c81c","03764b657f23996e31c64a701facc1cbeb0c9edfdd605e2c1ed36cf48197565d45"],"m/1/0/17":["020445179c522295b89bf4bfd582eb03422e3fa20dcd29263925e9f44282d476d8","036e47bdd32f3061aed1c1f8c2a32b038c7b72391cb1f80ebfc150e58f88372766"],"m/1/0/18":["024d88c4bfcbba713d49e1edcd035234aaa1ee76ad7bcf75bf074a16658a6b0b6d","02b861e7a20d89f6875d2e44c78dbadb99503e282e5e60e9f65657af6fea81d425"],"m/1/0/19":["023a8ca9d5300181f157e1930d3b0800eebe7683d8df72e6cbf28834dbf1be5d60","026053c4f84c10d15890c0b254522972931bc2d5b7cdf9c1f9f3137c22edf3ecd3"],"m/1/0/20":["03137c66e9f3d61aba659f408d77a293fa0f3fea4ccb911074a681d6f61a55d023","0291aa1bbfbef59b16b0e37e185a706c589d448cb02e860c5df9c9d7242ecc739f"],"m/1/0/21":["03c08673e0cae55318bc9dcc4b5f11eb3ff71d42de04015e255dde3fd8cba7e09e","02423d4eab06cd5b26e71d145283523c011d58032700c517f00b328d2c90cf109f"]}},"txProposals":{"txps":[{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543144016,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543144645},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543170040},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/0"],"comment":"blablabla","builderObj":{"valueInSat":"29000000","valueOutSat":"8900000","feeSat":"10000","remainderSat":"20090000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":0,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a9010000009300493046022100ccbb8f398f74a76236629b8499ffc6f9518a2091f5a61a9a352c0a10f615961e022100b8f0769c76cf33bec3d7f81d9da2b74cf6e8a5e0a24ee5f48172854d8bcdbfa101475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff02a0cd8700000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac908c32010000000017a914560c292066792531164149c5ed63ad2793a61b928700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543188745,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543189341},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543206819},"rejectedBy":{},"sentTs":1405543207304,"sentTxid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","inputChainPaths":["m/45\'/0/0/0"],"comment":"que parece","builderObj":{"valueInSat":"29000000","valueOutSat":"9000000","feeSat":"10000","remainderSat":"19990000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":1,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a901000000da00483045022035423cc74824ba904907678dda3b62a20a787b96d1b3e9f3e9546f9c57f4e45902210080a1ff1c39f458ac1642b9e948bd62fd70563b5252e749cc8fc642cd763ee830014730440220524a13f36cfb03caa246d7d84de634ec9386f2c39c19bfa926037f48da86262b022050e58a6503d105ad2805f86806810a1aa7f20d6271e1340b42fa91ab6a30f3e801475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff0240548900000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf00531010000000017a9146130a9d51f996b7a1b9d3e10c80930834251909d8700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543505848,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"rejectedBy":{},"sentTs":1405543610315,"sentTxid":"6fe851b54b777a75fe80fa204dc674395e2af69efb1f7c0017e909eb82c3d914","inputChainPaths":["m/45\'/0/1/1"],"comment":"mandaaaaaaa","builderObj":{"valueInSat":"19990000","valueOutSat":"19980000","feeSat":"10000","remainderSat":"0","hashToScriptMap":{"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts":"5221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52ae"},"selectedUtxos":[{"address":"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts","txid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","vout":1,"ts":1405543157,"scriptPubKey":"a9146130a9d51f996b7a1b9d3e10c80930834251909d87","amount":0.1999,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001664f1a8f905e6a6a2ec5f63abd3520840e21541ea8eb4e72272edd9326c99b1601000000db0048304502206b18b3dba2646c552469d8ef52d7656f6a65f563032530f622abdfd8bd4c5cee022100e804b406eddebbc827646141e74dc64c76a770ed4e35183ffd35d265ad9f7d3b01483045022100f6c013638ff0a316b1baa93dfffba6a98cf3033c133e8bd899e933c9c3e47ce10220530f40e7ea52ae58bec695edbec6d566d2ee8e7b5f33f95e33093ad1e29a125401475221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52aeffffffff01e0de3001000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac00000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543781381,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543782017},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381},"rejectedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543794590},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"1","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c000000009200483045022064d877bc5171fbaef909c2a1a924e0023b3ccc0b530cb46653f06ecb230283e8022100bc6658d60ad4f7120d9226c8f6eada87f3b0388f73c458011988bab36e78ba15014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a91421c4a435d9ac263ec55b35a1a5ca95e979639b9b8700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543835343,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543835968},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543850998},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"2","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c0000000092004830450220302baae7de2e0f102bf3af2d5f450f673e51bd143020141a769ccdcdf16af188022100e7abc087c76050ed649e7139a5a136969e74e24a8d8f6223d3219ad033a26451014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a9148b102abba0729fb0690c61cf7187064d692d43d78700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543869803,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543870411},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543890406},"rejectedBy":{},"sentTs":1405543890913,"sentTxid":"6a0f61574ad65e537e7e99298968db565f97b894b61f4c8f8fac8fcaedb83e2b","inputChainPaths":["m/45\'/0/0/1"],"comment":"3","builderObj":{"valueInSat":"29000000","valueOutSat":"1100000","feeSat":"10000","remainderSat":"27890000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c00000000db00483045022100a8ce7907f9fd7dd41dd65c2dec425e008efea06ee7c80787c10c0e210fbf181302207712c0fdd1cb25836ac1fc2fd303c1e26b85e8980417719b9ed50e977a9693ec01483045022100d1780c4f028cd898920aca3eaceba352ed9306cd17f019ae2f634e8facad149a02203c84ab2093da8e22577e93f27a732f0728d4e6db0c749f3cd3d898d6a025152a014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff02e0c81000000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac5091a9010000000017a914cc1cab78458b1a951b91c6dcd7eeeeb682f506388700000000"}}],"walletId":"55d4bd062d32f90a","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPdWUAmaaopPftevC72Jtiu19V8ee5XijL9JvogqfR95uVrL85f8yBdQMq3KyQtG3Q91yWQb3XDbWWpcdWFDAmJ7Xy2XWkGJu","networkName":"testnet","privateKeyCache":{"m/45\'/0/0/0":"b6fd8d1a079efd523da34f31ba81f544fc3d0a728a8a98299d8980682518e79c","m/45\'/0/1/1":"0f4d52d2a99e4c8c1c2edf09fef12407c3abd2304b961198c3f131a8c8443a13","m/45\'/0/0/1":"de5c191c343bd6017b98708c03344849624a14e2c167cfd6eb8dcb075d139293"}},"addressBook":{"msj42CCGruhRsFrGATiUuh25dtxYtnpbTx":{"hidden":false,"createdTs":1405543109222,"copayerId":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","label":"faucet","signature":"3045022067576e5b37f2707a8dc66e57511ad9b10a3125bd95193fff6f8f6402969c3bf3022100adff9f417db07d88face13b3d13f422740d4421440cade1a205684dfdc5d733a"}}}'; - - - -var legacy1 = '{"opts":{"id":"48ba2f1ffdfe9708","spendUnconfirmed":true,"requiredCopayers":1,"totalCopayers":1,"name":"pepe wallet","version":"0.4.7"},"networkNonce":"5405f06b00000001","networkNonces":[],"publicKeyRing":{"walletId":"48ba2f1ffdfe9708","networkName":"testnet","requiredCopayers":1,"totalCopayers":1,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1},{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}],"copayersBackup":["0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f"],"copayersExtPubKeys":["tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"48ba2f1ffdfe9708","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm","networkName":"testnet"},"addressBook":{}}'; From d84808f0c7ffaac6bb3dfc18cd2ccbaf602ed549 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 08:16:58 -0300 Subject: [PATCH 020/182] add test to join wallet --- test/test.Identity.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.Identity.js b/test/test.Identity.js index 171317ca6..8009bd85b 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -447,5 +447,6 @@ describe('Identity model', function() { }); }); }); +}); From 606ea0668c6496b36164295e6aba137d354917fd Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 15:28:02 -0300 Subject: [PATCH 021/182] refactor iden #create --- js/controllers/home.js | 12 ++++++------ js/models/Identity.js | 23 ++++++++++++----------- js/models/PluginManager.js | 4 ++-- js/models/Profile.js | 13 ++++++++++++- js/routes.js | 4 ++++ js/services/identity.js | 5 ----- plugins/GoogleDrive.js | 2 +- plugins/LocalStorage.js | 2 +- test/test.Identity.js | 24 ++++++++++-------------- views/home.html | 37 ++++++++++++++++++++++++------------- 10 files changed, 72 insertions(+), 54 deletions(-) diff --git a/js/controllers/home.js b/js/controllers/home.js index 29e736589..64580f85f 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -1,12 +1,12 @@ 'use strict'; -angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, identity, notification, controllerUtils) { +angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils) { controllerUtils.redirIfLogged(); - $scope.retreiving = true; - identity.getWallets(function(err,ret) { - $scope.retreiving = false; - $scope.hasWallets = (ret && ret.length > 0) ? true : false; - }); + //$scope.retreiving = true; + // identity.getWallets(function(err,ret) { + // $scope.retreiving = false; + // $scope.hasWallets = (ret && ret.length > 0) ? true : false; + // }); }); diff --git a/js/models/Identity.js b/js/models/Identity.js index 04f00410f..f64923079 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -57,15 +57,12 @@ function Identity(email, password, opts) { // open wallets this.wallets = []; - this.profile = Identity._newProfile({ - email: email, - }, password, this.storage); }; /* for stubbing */ -Identity._newProfile = function(info, password, storage) { - return new Profile(info, password, storage); +Identity._createProfile = function(email, password, storage, cb) { + Profile.create(email, password, storage, cb); }; Identity._newInsight = function(opts) { @@ -93,7 +90,7 @@ Identity._walletDelete = function(id, cb) { }; Identity._profileOpen = function(e, p, s, cb) { - return Profile.open(e, p, s, cb); + Profile.create(e, p, s, cb); }; @@ -109,11 +106,13 @@ Identity._profileOpen = function(e, p, s, cb) { * @return {undefined} */ Identity.create = function(email, password, opts, cb) { + var iden = new Identity(email, password, opts); - iden.store({ - overwrite: false, - }, function(err) { - return cb(err, iden); + Identity._createProfile(email, password, iden.storage, function(err, profile) { + if (err) return cb(err); + + iden.profile = profile; + return cb(null, iden); }); }; @@ -144,7 +143,7 @@ Identity.prototype.validate = function(authcode, cb) { Identity.open = function(email, password, opts, cb) { var iden = new Identity(email, password, opts); - Identity._profileOpen(email, password, iden.storage, function(err, profile){ + Identity._profileOpen(email, password, iden.storage, function(err, profile) { if (err) return cb(err); iden.profile = profile; @@ -174,6 +173,8 @@ Identity.isAvailable = function(email, opts, cb) { * @return {undefined} */ Identity.prototype.store = function(opts, cb) { + preconditions.checkState(this.profile); + var self = this; self.profile.store(opts, function(err) { if (err) return cb(err); diff --git a/js/models/PluginManager.js b/js/models/PluginManager.js index 6a58b6eb6..e13de65d4 100644 --- a/js/models/PluginManager.js +++ b/js/models/PluginManager.js @@ -31,8 +31,8 @@ PluginManager.prototype._register = function(obj, name) { var type = obj.type; var kind = PluginManager.TYPE[type]; - preconditions.checkArgument(kind, 'Plugin has unknown type' + name); - preconditions.checkState(kind !== PluginManager.KIND_UNIQUE || !this.registered[type], 'Plugin kind already registered: ' + name); + preconditions.checkArgument(kind, 'Unknown plugin type:' + name); + preconditions.checkState(kind !== PluginManager.KIND_UNIQUE || !this.registered[type], 'Plugin kind already registered:' + name); if (kind === PluginManager.KIND_UNIQUE) { this.registered[type] = obj; diff --git a/js/models/Profile.js b/js/models/Profile.js index dd8ee7f0d..8e20437ac 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -24,7 +24,18 @@ Profile.hash = function(email, password) { }; Profile.key = function(hash) { - return 'identity::' + hash; + return 'profile::' + hash; +}; + + +Profile.create = function(email, password, storage, cb) { + preconditions.checkArgument(cb); + var p = new Profile({ + email: email, + hash: Profile.hash(email,password), + }, storage); + + p.store(cb); }; Profile.open = function(email, password, storage, cb) { diff --git a/js/routes.js b/js/routes.js index 6f01ffc23..f0c5e9204 100644 --- a/js/routes.js +++ b/js/routes.js @@ -10,6 +10,10 @@ angular templateUrl: 'views/home.html', validate: false }) + .when('/createProfile', { + templateUrl: 'views/createProfile.html', + validate: false + }) .when('/open', { templateUrl: 'views/open.html', validate: false diff --git a/js/services/identity.js b/js/services/identity.js index 8cd37725e..e69de29bb 100644 --- a/js/services/identity.js +++ b/js/services/identity.js @@ -1,5 +0,0 @@ -'use strict'; -angular.module('copayApp.services').factory('identity', function(pluginManager){ - return new copay.Identity(config, copay.version, pluginManager); -}); - diff --git a/plugins/GoogleDrive.js b/plugins/GoogleDrive.js index 4c8783ac6..00417cdd6 100644 --- a/plugins/GoogleDrive.js +++ b/plugins/GoogleDrive.js @@ -12,7 +12,7 @@ function GoogleDrive(config) { this.home = config.home || 'copay'; this.idCache = {}; - this.type = 'STORAGE'; + this.type = 'DB'; this.scripts = [{ then: this.initLoaded.bind(this), diff --git a/plugins/LocalStorage.js b/plugins/LocalStorage.js index 0035fc12b..edad56006 100644 --- a/plugins/LocalStorage.js +++ b/plugins/LocalStorage.js @@ -1,7 +1,7 @@ 'use strict'; function LocalStorage() { - this.type = 'STORAGE'; + this.type = 'DB'; }; LocalStorage.prototype.init = function() { diff --git a/test/test.Identity.js b/test/test.Identity.js index 8009bd85b..0e08da85d 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -30,7 +30,7 @@ function assertObjectEqual(a, b) { describe('Identity model', function() { var iden, storage, wallet, profile; - beforeEach(function() { + beforeEach(function(done) { storage = sinon.stub(); storage.getItem = sinon.stub(); storage.setPassphrase = sinon.spy(); @@ -52,11 +52,14 @@ describe('Identity model', function() { profile.listWallets = sinon.stub().returns([]); profile.setLastOpenedTs = sinon.stub().yields(null);; profile.store = sinon.stub().yields(null);; - Identity._newProfile = sinon.stub().returns(profile); + Identity._createProfile = sinon.stub().callsArgWith(3,null,profile); - iden = new Identity(email, password, config); + Identity.create(email, password, config, function(err,i){ + iden = i; + done(); + }); }); @@ -105,25 +108,18 @@ describe('Identity model', function() { var iden = new Identity(email, password, config); should.exist(iden); iden.walletDefaults.should.deep.equal(config.wallet); - iden.version.should.equal('0.0.1'); - should.exist(iden.profile.addWallet); - - Identity._newProfile.getCall(0).args[0].should.deep.equal({ - email: email - }); - Identity._newProfile.getCall(0).args[1].should.equal(password); - Identity._newProfile.getCall(0).args[2].should.equal(iden.storage); }); }); describe('#create', function(done) { it('should call .store', function(done) { Identity.create(email, password, config, function(err, iden) { + should.not.exist(err); should.exist(iden.profile.addWallet); - iden.profile.store.getCall(0).args[0].should.deep.equal({ - overwrite: false - }); + + Identity._createProfile.getCall(0).args[0].should.deep.equal(email); + Identity._createProfile.getCall(0).args[1].should.deep.equal(password); done(); }); }); diff --git a/views/home.html b/views/home.html index 3c7f58533..3570e6b9d 100644 --- a/views/home.html +++ b/views/home.html @@ -1,4 +1,7 @@
+ +

( 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... @@ -8,25 +11,33 @@ Copay
-
- - +
-
- Create a wallet +

Login

+
+
+ + + + +
+
+ +
+
+ + + +
From 508a8bfc3cc24b3c326242dbb368b1171a1099f0 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 16:04:17 -0300 Subject: [PATCH 022/182] one wallet by default --- config.js | 2 +- js/models/Identity.js | 19 +++++++++++++++++-- js/models/Profile.js | 6 +++--- js/models/Storage.js | 2 +- test/test.Identity.js | 4 ++-- test/test.Profile.js | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/config.js b/config.js index 35e3d9b14..13f6db28e 100644 --- a/config.js +++ b/config.js @@ -3,7 +3,7 @@ var defaultConfig = { defaultLanguage: 'en', // DEFAULT network (livenet or testnet) networkName: 'livenet', - logLevel: 'info', + logLevel: 'debug', // wallet limits diff --git a/js/models/Identity.js b/js/models/Identity.js index f64923079..ebedda4ac 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -106,13 +106,28 @@ Identity._profileOpen = function(e, p, s, cb) { * @return {undefined} */ Identity.create = function(email, password, opts, cb) { + opts = opts || {}; var iden = new Identity(email, password, opts); + Identity._createProfile(email, password, iden.storage, function(err, profile) { if (err) return cb(err); - iden.profile = profile; - return cb(null, iden); + + if (opts.noWallets) + cb(null, iden); + + // default wallet + var wopts = { + nickname: email, + networkName: opts.networkName, + requiredCopayers: 1, + totalCopayers: 1, + }; + + iden.createWallet(wopts, function(err, w) { + return cb(null, iden, w); + }); }); }; diff --git a/js/models/Profile.js b/js/models/Profile.js index 8e20437ac..540149e80 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.getItem); + preconditions.checkArgument(storage.setPassphrase, 'bad storage'); this.hash = info.hash; this.email = info.email; @@ -34,8 +34,7 @@ Profile.create = function(email, password, storage, cb) { email: email, hash: Profile.hash(email,password), }, storage); - - p.store(cb); + p.store({}, cb); }; Profile.open = function(email, password, storage, cb) { @@ -118,6 +117,7 @@ Profile.prototype.store = function(opts, cb) { var key = self.key; self.storage.get(key, function(val2) { + if (val2 && !opts.overwrite) { if (cb) return cb(new Error('PEXISTS: Profile already exist')) diff --git a/js/models/Storage.js b/js/models/Storage.js index 0f576722f..faf46f1d4 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -27,7 +27,7 @@ function Storage(opts) { this.db = opts.db || localStorage; this.sessionStorage = opts.sessionStorage || sessionStorage; } catch (e) { - console.log('Error in storage:', e); //TODO + console.log('Error in storage:', e); }; preconditions.checkState(this.db, 'No db defined'); diff --git a/test/test.Identity.js b/test/test.Identity.js index 0e08da85d..4b361f2ed 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -191,7 +191,7 @@ describe('Identity model', function() { iden.createWallet({ privateKeyHex: priv, }, function(err, w) { - Identity._newWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + Identity._newWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); should.not.exist(err); done(); }); @@ -253,7 +253,7 @@ describe('Identity model', function() { iden.openWallet('dummy', 'xxx', function(err, w) { should.not.exist(err); w.store.calledOnce.should.equal(true); - iden.profile.setLastOpenedTs.calledOnce.should.equal(true); + iden.profile.setLastOpenedTs.calledTwice.should.equal(true); iden.migrateWallet.calledOnce.should.equal(true); done(); }); diff --git a/test/test.Profile.js b/test/test.Profile.js index af06fc544..e7f90dd9b 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -19,7 +19,7 @@ describe('Profile model', function() { }; beforeEach(function() { - storage.getItem = sinon.stub(); + storage.setPassphrase = sinon.stub(); storage.set = sinon.stub(); storage.set.yields(null); storage.get = sinon.stub().yields(null); From 70d306242e1c24b06cf5a3c9b04a9c0a1e13e08f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 20:12:02 -0300 Subject: [PATCH 023/182] refactors to UX --- js/models/Identity.js | 29 +++++++++++++++-------------- js/models/Profile.js | 8 +++++++- js/models/Storage.js | 5 +++++ js/models/Wallet.js | 15 +++++++++++---- test/test.Identity.js | 6 +++--- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index ebedda4ac..c7e5f9a05 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -11,6 +11,7 @@ var version = require('../../version').version; var PluginManager = require('./PluginManager'); var Profile = require('./Profile'); var Insight = module.exports.Insight = require('./Insight'); +var Async = module.exports.Async = require('./Async'); var preconditions = require('preconditions').singleton(); var Storage = module.exports.Storage = require('./Storage'); @@ -44,8 +45,8 @@ function Identity(email, password, opts) { this.storage = Identity._newStorage(storageOpts); this.networks = { - 'livenet': Identity._newInsight(opts.network.livenet), - 'testnet': Identity._newInsight(opts.network.testnet), + 'livenet': Identity._newAsync(opts.network.livenet), + 'testnet': Identity._newAsync(opts.network.testnet), }; this.blockchains = { 'livenet': Identity._newInsight(opts.network.livenet), @@ -69,6 +70,12 @@ Identity._newInsight = function(opts) { return new Insight(opts); }; +Identity._newAsync = function(opts) { + return new Async(opts); +}; + + + Identity._newStorage = function(opts) { return new Storage(opts); }; @@ -89,13 +96,6 @@ Identity._walletDelete = function(id, cb) { return Wallet.delete(id, cb); }; -Identity._profileOpen = function(e, p, s, cb) { - Profile.create(e, p, s, cb); -}; - - - - /** * creates and Identity * @@ -113,18 +113,18 @@ Identity.create = function(email, password, opts, cb) { Identity._createProfile(email, password, iden.storage, function(err, profile) { if (err) return cb(err); iden.profile = profile; +console.log('[Identity.js.115:profile:]',profile); //TODO if (opts.noWallets) cb(null, iden); // default wallet - var wopts = { + var wopts = _.extend(opts.walletDefaults,{ nickname: email, networkName: opts.networkName, requiredCopayers: 1, totalCopayers: 1, - }; - + }); iden.createWallet(wopts, function(err, w) { return cb(null, iden, w); }); @@ -158,10 +158,9 @@ Identity.prototype.validate = function(authcode, cb) { Identity.open = function(email, password, opts, cb) { var iden = new Identity(email, password, opts); - Identity._profileOpen(email, password, iden.storage, function(err, profile) { + Identity._createProfile(email, password, iden.storage, function(err, profile) { if (err) return cb(err); iden.profile = profile; - return cb(null, iden); }); }; @@ -254,6 +253,7 @@ Identity.prototype.importWallet = function(base64, passphrase, skipFields, cb) { */ Identity.prototype.createWallet = function(opts, cb) { preconditions.checkArgument(cb); + preconditions.checkState(this.profile); opts = opts || {}; opts.networkName = opts.networkName || 'testnet'; @@ -318,6 +318,7 @@ Identity.prototype.addWallet = function(wallet, cb) { preconditions.checkArgument(wallet); preconditions.checkArgument(wallet.getId); preconditions.checkArgument(cb); + preconditions.checkState(this.profile); var self = this; self.profile.addWallet(wallet.id, function(err) { diff --git a/js/models/Profile.js b/js/models/Profile.js index 540149e80..4c857c2c1 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -30,11 +30,17 @@ Profile.key = function(hash) { Profile.create = function(email, password, storage, cb) { preconditions.checkArgument(cb); + preconditions.checkArgument(storage.setPassphrase); + + preconditions.checkState(storage.hasPassphrase()); + var p = new Profile({ email: email, hash: Profile.hash(email,password), }, storage); - p.store({}, cb); + p.store({}, function(err) { + return cb(err,p); + }); }; Profile.open = function(email, password, storage, cb) { diff --git a/js/models/Storage.js b/js/models/Storage.js index faf46f1d4..9880d31d0 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -43,6 +43,11 @@ Storage.prototype._getPassphrase = function() { return pps[this.__uniqueid]; } + +Storage.prototype.hasPassphrase = function() { + return pps[this.__uniqueid] ? true : false; +}; + Storage.prototype.setPassphrase = function(password) { pps[this.__uniqueid] = password; } diff --git a/js/models/Wallet.js b/js/models/Wallet.js index f628e3f6f..fa96c8653 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -79,8 +79,6 @@ function Wallet(opts) { this.publicKeyRing.walletId = this.id; this.txProposals.walletId = this.id; - this.network.maxPeers = this.totalCopayers; - this.network.secretNumber = this.secretNumber; this.registeredPeerIds = []; this.addressBook = opts.addressBook || {}; this.publicKey = this.privateKey.publicHex; @@ -96,6 +94,15 @@ function Wallet(opts) { this.paymentRequests = opts.paymentRequests || {}; + + var networkName = Wallet.obtainNetworkName(this); + this.network = _.isArray(this.network)? this.network[networkName] : this.network; + this.blockchain = _.isArray(this.blockchain) ? this.blockchain[networkName] : this.blockchain; + + + this.network.maxPeers = this.totalCopayers; + this.network.secretNumber = this.secretNumber; + //network nonces are 8 byte buffers, representing a big endian number //one nonce for oneself, and then one nonce for each copayer this.network.setHexNonce(opts.networkNonce); @@ -1064,8 +1071,8 @@ Wallet.fromObj = function(o, storage, network, blockchain, skipFields) { opts.lastTimestamp = o.lastTimestamp; opts.storage = storage; - opts.network = _.isArray(network)? network[networkName] : network; - opts.blockchain = _.isArray(blockchain) ? blockchain[networkName] : blockchain; + opts.network = network; + opts.blockchain = blockchain; opts.isImported = true; return new Wallet(opts); diff --git a/test/test.Identity.js b/test/test.Identity.js index 4b361f2ed..25aabe7ee 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -127,14 +127,14 @@ describe('Identity model', function() { describe('#open', function(done) { beforeEach(function() { - Identity._profileOpen = sinon.stub().callsArgWith(3, null, 'kk'); + Identity._createProfile = sinon.stub().callsArgWith(3, null, 'kk'); }); - it('should call ._profileOpen', function(done) { + it('should call ._createProfile', function(done) { Identity.open(email, password, config, function(err, iden) { should.not.exist(err); iden.profile.should.equal('kk'); - Identity._profileOpen.calledOnce.should.equal(true); + Identity._createProfile.calledOnce.should.equal(true); done(); }); }); From 74129a69235e7c49483034272d343939c80be0d8 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 20:29:56 -0300 Subject: [PATCH 024/182] mv Passphrase logic to Storage --- js/models/Identity.js | 1 - js/models/Storage.js | 20 ++++++++++++++++---- test/Storage.js | 6 +++--- test/mocks/FakeLocalStorage.js | 1 + 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index c7e5f9a05..d026c3ea1 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -113,7 +113,6 @@ Identity.create = function(email, password, opts, cb) { Identity._createProfile(email, password, iden.storage, function(err, profile) { if (err) return cb(err); iden.profile = profile; -console.log('[Identity.js.115:profile:]',profile); //TODO if (opts.noWallets) cb(null, iden); diff --git a/js/models/Storage.js b/js/models/Storage.js index 9880d31d0..c74211d51 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -2,6 +2,7 @@ var preconditions = require('preconditions').singleton(); var CryptoJS = require('node-cryptojs-aes').CryptoJS; var bitcore = require('bitcore'); +var Passphrase = require('./Passphrase'); var preconditions = require('preconditions').instance(); var _ = require('underscore'); var CACHE_DURATION = 1000 * 60 * 5; @@ -17,11 +18,15 @@ var id = 0; function Storage(opts) { preconditions.checkArgument(opts); preconditions.checkArgument(opts.password); - opts = opts || {}; this.wListCache = {}; this.__uniqueid = ++id; - this.setPassphrase(opts.password); + this.passphraseConfig = { + salt: opts.salt, + iterations: opts.iterations, + }; + + this.setPassword(opts.password); try { this.db = opts.db || localStorage; @@ -48,8 +53,15 @@ Storage.prototype.hasPassphrase = function() { return pps[this.__uniqueid] ? true : false; }; -Storage.prototype.setPassphrase = function(password) { - pps[this.__uniqueid] = password; + +Storage.prototype._setPassphrase = function(passphrase) { + pps[this.__uniqueid] = passphrase; +}; + +Storage.prototype.setPassword = function(password, config) { + var passphraseConfig = _.extend(this.passphraseConfig, config); + var p = new Passphrase(passphraseConfig); + this._setPassphrase(p.getBase64(password)); } Storage.prototype._encrypt = function(string) { diff --git a/test/Storage.js b/test/Storage.js index 74b95fb10..842169528 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -9,7 +9,7 @@ describe('Storage model', function() { var s; beforeEach(function(done) { s = new Storage(requireMock('FakeLocalStorage').storageParams); - s.setPassphrase('mysupercoolpassword'); + s.setPassword('mysupercoolpassword'); s.clearAll(done); }); @@ -415,13 +415,13 @@ describe('Storage model', function() { describe('#decrypt', function() { it('should not be able to decrypt with wrong password', function() { - s.setPassphrase('xxx'); + s.setPassword('xxx'); var wo = s.decrypt(encryptedLegacy1); should.not.exist(wo); }); it('should be able to decrypt an old backup', function() { - s.setPassphrase(legacyPassword1); + s._setPassphrase(legacyPassword1); var wo = s.decrypt(encryptedLegacy1); should.exist(wo); wo.opts.id.should.equal('48ba2f1ffdfe9708'); diff --git a/test/mocks/FakeLocalStorage.js b/test/mocks/FakeLocalStorage.js index 56a6b5aab..6fad99249 100644 --- a/test/mocks/FakeLocalStorage.js +++ b/test/mocks/FakeLocalStorage.js @@ -32,4 +32,5 @@ module.exports.storageParams = { password: '123', db: new FakeLocalStorage(), sessionStorage: new FakeLocalStorage(), + iterations: 1, }; From 7a2906c8d106876b41be3f740fbab85877eae47c Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 30 Sep 2014 21:16:46 -0300 Subject: [PATCH 025/182] create profile working! --- js/controllers/copayers.js | 4 +-- js/controllers/createProfile.js | 24 +++++++++++++ js/models/Identity.js | 63 +++++++++++++++++++++++---------- js/models/Profile.js | 10 +++--- js/models/Storage.js | 9 +++-- js/models/Wallet.js | 8 ++--- js/services/controllerUtils.js | 8 ++--- test/PayPro.js | 2 +- test/Wallet.js | 4 +-- test/WalletLock.js | 2 +- test/mocks/FakeLocalStorage.js | 4 ++- views/createProfile.html | 44 +++++++++++++++++++++++ 12 files changed, 139 insertions(+), 43 deletions(-) create mode 100644 js/controllers/createProfile.js create mode 100644 views/createProfile.html 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 +
+
+
+
+ + From 92f1bacf82516d51ed9dada27ad29cac9686b23c Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 1 Oct 2014 08:35:17 -0300 Subject: [PATCH 026/182] login working on the UX --- js/controllers/home.js | 28 ++++++++++++++++++++++------ js/models/Identity.js | 40 +++++++++++++++++++++++++++------------- js/models/Profile.js | 28 +++++++++++++++------------- js/models/Storage.js | 2 +- js/models/Wallet.js | 17 ++++++++++++----- test/PayPro.js | 12 +++++++++++- test/Wallet.js | 21 +++++++++++---------- test/test.Identity.js | 36 +++++++++++++++++++----------------- test/test.Profile.js | 2 +- views/home.html | 11 ++++++----- 10 files changed, 125 insertions(+), 72 deletions(-) diff --git a/js/controllers/home.js b/js/controllers/home.js index 64580f85f..39c28adb1 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -1,12 +1,28 @@ 'use strict'; -angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils) { +angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) { controllerUtils.redirIfLogged(); - //$scope.retreiving = true; - // identity.getWallets(function(err,ret) { - // $scope.retreiving = false; - // $scope.hasWallets = (ret && ret.length > 0) ? true : false; - // }); + $scope.openProfile = function(form) { + $scope.loading = true; + copay.Identity.open(form.email.$modelValue, form.password.$modelValue, { + pluginManager: pluginManager, + network: config.network, + networkName: config.networkName, + walletDefaults: config.wallet, + passphrase: config.passphrase, + }, function(err, iden, w) { + if (err && !w) { + console.log('Error:' + err) + controllerUtils.onErrorDigest( + $scope, (err.toString()||'').match('PNOTFOUND') ? 'Profile not found' : 'Unknown error'); + } else { + $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 05bb82df7..a3865f340 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -98,6 +98,13 @@ Identity._walletDelete = function(id, cb) { return Wallet.delete(id, cb); }; +/* for stubbing */ +Identity._openProfile = function(email, password, storage, cb) { + Profile.open(email, password, storage, cb); +}; + + + /** * creates and Identity * @@ -160,10 +167,14 @@ Identity.prototype.validate = function(authcode, cb) { Identity.open = function(email, password, opts, cb) { var iden = new Identity(email, password, opts); - Identity._createProfile(email, password, iden.storage, function(err, profile) { + Identity._openProfile(email, password, iden.storage, function(err, profile) { if (err) return cb(err); iden.profile = profile; - return cb(null, iden); + var wid = iden.listWallets()[0].id; + iden.openWallet(wid, password, function(err, w) { + return cb(err, iden, w); + }) + }); }; @@ -217,15 +228,17 @@ Identity.prototype.store = function(opts, cb) { Identity.prototype.close = function(cb) { preconditions.checkState(this.profile); - var l = self.openWallets.length, + var l = this.openWallets.length, i = 0; - if (!l) return cb(); + if (!l) { + return cb ? cb() : null; + } - _.each(self.openWallets, function(w) { + _.each(this.openWallets, function(w) { w.close(function(err) { if (err) return cb(err); - if (++i == l) + if (++i == l && cb) return cb(); }) }); @@ -390,18 +403,19 @@ Identity.prototype.openWallet = function(walletId, password, cb) { var self = this; self.storage.setPassword(password); - self.migrateWallet(walletId, password, function() { + // TODO + // self.migrateWallet(walletId, password, function() { - Identity._walletRead(walletId, self.storage, self.networks, self.blockchains, [], function(err, w) { - if (err) return cb(err); + Identity._walletRead(walletId, self.storage, self.networks, self.blockchains, [], function(err, w) { + if (err) return cb(err); - w.store(function(err) { - self.profile.setLastOpenedTs(walletId, function() { - return cb(err, w); - }); + w.store(function(err) { + self.profile.setLastOpenedTs(walletId, function() { + return cb(err, w); }); }); }); + // }); }; diff --git a/js/models/Profile.js b/js/models/Profile.js index 69113c5cf..83b810c48 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -13,9 +13,9 @@ function Profile(info, storage) { this.hash = info.hash; this.email = info.email; this.extra = info.extra; + this.walletInfos = info.walletInfos || {}; this.key = Profile.key(this.hash); - this.walletInfos = {}; this.storage = storage; }; @@ -36,29 +36,31 @@ Profile.create = function(email, password, storage, cb) { var p = new Profile({ email: email, - hash: Profile.hash(email,password), + hash: Profile.hash(email, password), }, storage); p.store({}, function(err) { - return cb(err,p); + return cb(err, p); }); }; Profile.open = function(email, password, storage, cb) { preconditions.checkArgument(cb); + preconditions.checkState(storage.hasPassphrase()); var key = Profile.key(Profile.hash(email, password)); - storage.getGlobal(key, function(err, val) { - if (err) return cb(err); - - if (!val) + storage.get(key, function(err, val) { + if (err || !val) return cb(new Error('PNOTFOUND: Profile not found')); - return cb(new Profile(val, storage)); + if (!val.email) + return cb(new Error('PERROR: Could not open profile')); + + return cb(null, new Profile(val, storage)); }); }; Profile.prototype.toObj = function() { - return JSON.parse(JSON.stringify(this)); + return _.clone(_.pick(this, 'hash', 'email', 'extra', 'walletInfos')); }; Profile.prototype.getWallet = function(walletId, cb) { @@ -74,7 +76,7 @@ Profile.prototype.listWallets = function(opts, cb) { Profile.prototype.deleteWallet = function(walletId, cb) { if (!this.walletInfos[walletId]) - return cb(new Error('WNOEXIST: Wallet not on profile')); + return cb(new Error('WNOEXIST: Wallet not on profile ')); delete this.walletInfos[walletId]; @@ -85,7 +87,7 @@ Profile.prototype.deleteWallet = function(walletId, cb) { Profile.prototype.addToWallet = function(walletId, info, cb) { if (!this.walletInfos[walletId]) - return cb(new Error('WNOEXIST: Wallet not on profile')); + return cb(new Error('WNOEXIST: Wallet not on profile ')); this.walletInfos[walletId] = _.extend(this.walletInfos[walletId], info); @@ -100,7 +102,7 @@ Profile.prototype.addWallet = function(walletId, info, cb) { preconditions.checkArgument(cb); if (this.walletInfos[walletId]) - return cb(new Error('WEXIST: Wallet already on profile')); + return cb(new Error('WEXIST: Wallet already on profile ')); this.walletInfos[walletId] = _.extend(info, { createdTs: Date.now(), @@ -128,7 +130,7 @@ Profile.prototype.store = function(opts, cb) { if (val2 && !opts.overwrite) { if (cb) - return cb(new Error('PEXISTS: Profile already exist')) + return cb(new Error('PEXISTS: Profile already exist ')) } else { self.storage.set(key, val, function(err) { log.debug('Profile stored'); diff --git a/js/models/Storage.js b/js/models/Storage.js index 9612a7c3d..0c4a4daa6 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -89,7 +89,7 @@ Storage.prototype._read = function(k, cb) { var self = this; this.db.getItem(k, function(ret) { if (!ret) return cb(null); - var ret = self._decrypt(ret); + ret = self._decrypt(ret); if (!ret) return cb(null); ret = ret.toString(CryptoJS.enc.Utf8); diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 7c2bdd92a..0b3ad2e82 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -71,6 +71,7 @@ function Wallet(opts) { self[k] = opts[k]; }); + this.id = opts.id || Wallet.getRandomId(); this.secretNumber = opts.secretNumber || Wallet.getRandomNumber(); this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin); @@ -94,10 +95,12 @@ function Wallet(opts) { this.paymentRequests = opts.paymentRequests || {}; + var networkName = Wallet.obtainNetworkName(opts); + this.network = networkName && this.network[networkName] ? this.network[networkName] : this.network; + this.blockchain = networkName && this.blockchain[networkName] ? this.blockchain[networkName] : this.blockchain; - var networkName = Wallet.obtainNetworkName(this); - this.network = _.isArray(this.network)? this.network[networkName] : this.network; - this.blockchain = _.isArray(this.blockchain) ? this.blockchain[networkName] : this.blockchain; + preconditions.checkArgument(this.network.setHexNonce, 'Incorrect network parameter'); + preconditions.checkArgument(this.blockchain.getTransaction, 'Incorrect blockchain parameter'); this.network.maxPeers = this.totalCopayers; @@ -217,8 +220,9 @@ Wallet.delete = function(walletId, storage, cb) { * @param {function} callback - {err, Wallet} * @return {undefined} */ -Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) { +Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) { preconditions.checkArgument(cb); + preconditions.checkArgument(storage.setPassword); var self = this, err; @@ -237,10 +241,12 @@ Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) var w, err; obj.id = walletId; try { +console.log('[Wallet.js.218:network:]',network); //TODO w = self.fromObj(obj, storage, network, blockchain, skipFields); } catch (e) { + log.debug("ERROR: ", e.message); if (e && e.message && e.message.indexOf('MISSOPTS')) { - err = new Error('WERROR: Could not read: ' + walletId); + err = new Error('WERROR: Could not read: ' + walletId + ': ' + e.message); } else { err = e; } @@ -1637,6 +1643,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { options = {}; } +console.log('[Wallet.js.1613:ntxid:]',ntxid); //TODO var txp = this.txProposals.get(ntxid); if (!txp) return; diff --git a/test/PayPro.js b/test/PayPro.js index c155311ed..f80c6c26f 100644 --- a/test/PayPro.js +++ b/test/PayPro.js @@ -83,6 +83,14 @@ describe('PayPro (in Wallet) model', function() { c.networkName = walletConfig.networkName; c.version = '0.0.1'; + c.network = sinon.stub(); + c.network.setHexNonce = sinon.stub(); + c.network.setHexNonces = sinon.stub(); + c.network.getHexNonce = sinon.stub(); + c.network.getHexNonces = sinon.stub(); + c.network.send = sinon.stub(); + + return new Wallet(c); } @@ -683,9 +691,11 @@ describe('PayPro (in Wallet) model', function() { }); }); - it('#add tx proposal based on payment message via model', function(done) { + it('#add tx proposal based on payment message via model ', function(done) { + var w = ppw; should.exist(w); + w.sendPaymentTx(w._ntxid, function(txid, merchantData) { should.exist(txid); should.exist(merchantData); diff --git a/test/Wallet.js b/test/Wallet.js index d4814c31a..0c59ed97b 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1894,12 +1894,13 @@ describe('Wallet model', function() { var storage = new s(); var network = new Network(walletConfig.network); var blockchain = new Blockchain(walletConfig.blockchain); + storage.setPassword = sinon.stub(); it('should fail to read an unexisting wallet', function(done) { - s.getFirst = sinon.stub().yields(null); + storage.getFirst = sinon.stub().yields(null); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + Wallet.read('123', storage, network, blockchain, [], function(err, w) { err.toString().should.contain('WNOTFOUND'); done(); }); @@ -1907,25 +1908,25 @@ describe('Wallet model', function() { it('should not read a corrupted wallet', function(done) { - s.getFirst = sinon.stub().yields(null, '{hola:1}'); + storage.getFirst = sinon.stub().yields(null, '{hola:1}'); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + Wallet.read('123', storage, network, blockchain, [], function(err, w) { err.toString().should.contain('WERROR'); done(); }); }); it('should read a wallet', function(done) { - s.getFirst = sinon.stub().yields(null, JSON.parse(o)); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + storage.getFirst = sinon.stub().yields(null, JSON.parse(o)); + Wallet.read('123', storage, network, blockchain, [], function(err, w) { should.not.exist(err); done(); }); }); it('should be able to import unencrypted legacy wallet TxProposal: v0', function(done) { - s.getFirst = sinon.stub().yields(null, JSON.parse(legacyO)); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + storage.getFirst = sinon.stub().yields(null, JSON.parse(legacyO)); + Wallet.read('123', storage, network, blockchain, [], function(err, w) { should.exist(w); w.id.should.equal('55d4bd062d32f90a'); should.exist(w.publicKeyRing.getCopayerId); @@ -1936,9 +1937,9 @@ describe('Wallet model', function() { }); it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function(done) { - s.getFirst = sinon.stub().yields(null, JSON.parse(legacy1)); + storage.getFirst = sinon.stub().yields(null, JSON.parse(legacy1)); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + Wallet.read('123', storage, network, blockchain, [], function(err, w) { should.exist(w); w.isReady().should.equal(true); var wo = w.toObj(); diff --git a/test/test.Identity.js b/test/test.Identity.js index 25aabe7ee..eeebacfeb 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -33,10 +33,10 @@ describe('Identity model', function() { beforeEach(function(done) { storage = sinon.stub(); storage.getItem = sinon.stub(); - storage.setPassphrase = sinon.spy(); + storage.setPassword = sinon.spy(); + storage.hasPassphrase = sinon.stub().returns(true); storage.getSessionId = sinon.spy(); storage.setFromObj = sinon.spy(); - storage.setLastOpened = sinon.stub().yields(null); storage.deletePrefix = sinon.stub().yields(null); Identity._newStorage = sinon.stub().returns(storage); @@ -127,14 +127,17 @@ describe('Identity model', function() { describe('#open', function(done) { beforeEach(function() { - Identity._createProfile = sinon.stub().callsArgWith(3, null, 'kk'); + storage.getFirst = sinon.stub().yields('wallet1234'); + profile.listWallets = sinon.stub().returns([{id:'walletid'}]); + Identity._openProfile = sinon.stub().callsArgWith(3, null, profile); + Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); }); - it('should call ._createProfile', function(done) { - Identity.open(email, password, config, function(err, iden) { + it('should call ._openProfile', function(done) { + Identity.open(email, password, config, function(err, iden, w) { + Identity._openProfile.calledOnce.should.equal(true); should.not.exist(err); - iden.profile.should.equal('kk'); - Identity._createProfile.calledOnce.should.equal(true); + iden.profile.should.equal(profile); done(); }); }); @@ -154,7 +157,7 @@ describe('Identity model', function() { it('should call .store from profile and wallets (2)', function(done) { iden.profile.store = sinon.stub().yields(null); - iden.wallets = [{ + iden.openWallets = [{ store: sinon.stub().yields(null) }, { store: sinon.stub().yields(null) @@ -162,8 +165,8 @@ describe('Identity model', function() { iden.store({}, function(err) { should.not.exist(err); iden.profile.store.calledOnce.should.equal(true); - iden.wallets[0].store.calledOnce.should.equal(true); - iden.wallets[1].store.calledOnce.should.equal(true); + iden.openWallets[0].store.calledOnce.should.equal(true); + iden.openWallets[1].store.calledOnce.should.equal(true); done(); }); }); @@ -226,8 +229,7 @@ describe('Identity model', function() { beforeEach(function() { iden.migrateWallet = sinon.stub().yields(null); - iden.profile.setLastOpened = sinon.stub().yields(null); - iden.storage.setPassphrase = sinon.spy(); + storage.setPassword = sinon.spy(); storage.getFirst = sinon.stub().yields('wallet1234'); var wallet = sinon.stub(); @@ -236,14 +238,14 @@ describe('Identity model', function() { Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); }); - it('should call setPassphrase', function(done) { + it('should call setPassword', function(done) { var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); iden.openWallet('id123', 'xxx', function(err, w) { - iden.storage.setPassphrase.calledOnce.should.equal(true); - iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + iden.storage.setPassword.calledOnce.should.equal(true); + iden.storage.setPassword.getCall(0).args[0].should.equal('xxx'); done(); }); }); @@ -254,7 +256,7 @@ describe('Identity model', function() { should.not.exist(err); w.store.calledOnce.should.equal(true); iden.profile.setLastOpenedTs.calledTwice.should.equal(true); - iden.migrateWallet.calledOnce.should.equal(true); + // iden.migrateWallet.calledOnce.should.equal(true); done(); }); }); @@ -332,7 +334,7 @@ describe('Identity model', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', nickname: 'test', - passphrase: 'pass' + password: 'pass' }; it('should yield bad network error', function(done) { diff --git a/test/test.Profile.js b/test/test.Profile.js index e7f90dd9b..3e2c87639 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -19,7 +19,7 @@ describe('Profile model', function() { }; beforeEach(function() { - storage.setPassphrase = sinon.stub(); + storage.setPassword = sinon.stub(); storage.set = sinon.stub(); storage.set.yields(null); storage.get = sinon.stub().yields(null); diff --git a/views/home.html b/views/home.html index 3570e6b9d..44ab7b0aa 100644 --- a/views/home.html +++ b/views/home.html @@ -1,7 +1,8 @@
-

( 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 ) +

( TODO: only show this login form if there is any profile:: key) + +

( TODO: 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... @@ -14,12 +15,12 @@

Login

-
+
- + - +
-
-
- Copay -
-
-
+ +

{{'Create Wallet'|translate}}

+
+
- Step 1 - Step 2 + Step 1

Create new wallet

-
diff --git a/views/home.html b/views/home.html index 10c72e0d2..698c94ad2 100644 --- a/views/home.html +++ b/views/home.html @@ -13,26 +13,23 @@
-
+

Login

-
-
- - - - -
+ + + +
-
- - - From 3f9686bb0422956252bc74d3849ca4504163ec1b Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 10 Oct 2014 10:45:29 -0300 Subject: [PATCH 037/182] creating a wallet --- css/src/main.css | 11 ++----- views/create.html | 75 +++++++++++++++++++++++------------------------ 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/css/src/main.css b/css/src/main.css index 23aa5db10..0d1d26205 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -159,10 +159,7 @@ input:-webkit-autofill, textarea:-webkit-autofill, select:-webkit-autofill, inpu } .home label, -.createProfile label, -.join label, -.open label, -.setup label { +.createProfile label { font-size: 0.875rem; color: #fff; font-weight: 100; @@ -194,10 +191,6 @@ a:hover { .home select, .createProfile select, -.open select, -.join select, -.setup select, -.import select, .settings select { background: #2C3E50 !important; border: 0 !important; @@ -254,7 +247,7 @@ a:hover { background-color: #F8F8FB; } -.home, .createProfile, .open, .join, .waiting-copayers, .setup, .import, .settings { +.home, .createProfile, .settings { margin-top: 15%; color: #fff; } diff --git a/views/create.html b/views/create.html index 868e7db12..3590c6dcf 100644 --- a/views/create.html +++ b/views/create.html @@ -7,48 +7,47 @@

{{'Create Wallet'|translate}}

-
-
-
- Step 1 -

Create new wallet

- -
-
- -
-
- -
+
+
+ +
+
+
-
-
- -
+
+
-

(*) The limits are imposed by the bitcoin network.

+
+
+
+ +
+
+

(*) The limits are imposed by the bitcoin network.

- - - Show - Hide - advanced options - -
- - + +
+ + -

- -

+

+ +

From 0c591ce9c06c9e84cfccec940b0a193fbecc671a Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 10 Oct 2014 11:01:22 -0300 Subject: [PATCH 038/182] waiting for copayers --- css/src/main.css | 3 --- views/copayers.html | 33 +++++++++------------------------ 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/css/src/main.css b/css/src/main.css index 0d1d26205..8a9430261 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -1159,13 +1159,10 @@ a.text-warning:hover {color: #FD7262;} } .panel .secret { - line-height: 1.3rem; - padding-top: 4rem; float: left; margin-left: 2rem; overflow-wrap: break-word; word-wrap: break-word; - width: 55%; text-align: left; } diff --git a/views/copayers.html b/views/copayers.html index 1de46a3bc..9b3e81013 100644 --- a/views/copayers.html +++ b/views/copayers.html @@ -1,17 +1,18 @@
-
-
- -
+
+
- Step 3 -

Waiting copayers

-

Share this secret with your other copayers

+

+ Waiting copayers for + {{$root.wallet.getName()}} + {{$root.wallet.requiredCopayers}}-{{'of'|translate}}-{{$root.wallet.totalCopayers}} +

-
+
+

Share this secret with your other copayers

{{$root.wallet.getSecret()}}
@@ -33,21 +34,6 @@
-
-
-

- Waiting Copayers for {{$root.wallet.getName()}} -

-
-
-

- - {{$root.wallet.requiredCopayers}}-{{'of'|translate}}-{{$root.wallet.totalCopayers}} - -

-
-
-

@@ -131,7 +117,6 @@ ng-show="hideViewBackup" ng-click="skipBackup()" translate>Continue

-
From 76e09a0bd4fe04d63608a49c6f1e7321360f049a Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 10 Oct 2014 11:06:56 -0300 Subject: [PATCH 039/182] wallet created --- views/copayers.html | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/views/copayers.html b/views/copayers.html index 9b3e81013..82553d0ec 100644 --- a/views/copayers.html +++ b/views/copayers.html @@ -19,21 +19,12 @@
-

New Wallet Created

-
-
-

Download Backup

-
-
-

- - {{$root.wallet.getName()}} : - {{$root.wallet.requiredCopayers}}-{{'of'|translate}}-{{$root.wallet.totalCopayers}} - -

-
-
+

+ Wallet {{$root.wallet.getName()}} + {{$root.wallet.requiredCopayers}}-{{'of'|translate}}-{{$root.wallet.totalCopayers}} + created +

From 006bf4c1fadaa001dda10533d4fdfd9964c8c2e4 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 10 Oct 2014 11:42:23 -0300 Subject: [PATCH 040/182] fix join --- js/models/Wallet.js | 21 +++++++++++++-------- views/join.html | 27 +++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 0bd9b8d93..c082db16c 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -837,14 +837,19 @@ Wallet.prototype.getSecret = function() { * @return {Object} */ Wallet.decodeSecret = function(secretB) { - var secret = Base58Check.decode(secretB); - var pubKeyBuf = secret.slice(0, 33); - var secretNumber = secret.slice(33, 38); - var networkName = secret.slice(38, 39).toString('hex') === '00' ? 'livenet' : 'testnet'; - return { - pubKey: pubKeyBuf.toString('hex'), - secretNumber: secretNumber.toString('hex'), - networkName: networkName, + try { + var secret = Base58Check.decode(secretB); + var pubKeyBuf = secret.slice(0, 33); + var secretNumber = secret.slice(33, 38); + var networkName = secret.slice(38, 39).toString('hex') === '00' ? 'livenet' : 'testnet'; + return { + pubKey: pubKeyBuf.toString('hex'), + secretNumber: secretNumber.toString('hex'), + networkName: networkName, + } + } catch (e) { + log.debug(e.message); + return false; } }; diff --git a/views/join.html b/views/join.html index a96d013d9..b10990e36 100644 --- a/views/join.html +++ b/views/join.html @@ -5,10 +5,8 @@

{{'Join Wallet'|translate}}

-
-
-
-

Join a Wallet in Creation

+
+
- - - Show - Hide - advanced options - -
+ +

@@ -65,12 +65,7 @@
- - - - -
From b5f05118c770d7bf7bfc9633e6d112e25566e576 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 10 Oct 2014 12:02:46 -0300 Subject: [PATCH 041/182] update models tests to new API --- js/models/Wallet.js | 15 ++++++++------- test/Wallet.js | 19 +++++++------------ test/models/Identity.js | 5 +++-- views/includes/sidebar.html | 4 ++++ 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/js/models/Wallet.js b/js/models/Wallet.js index c082db16c..8b206ccea 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -5,6 +5,7 @@ var _ = require('underscore'); var preconditions = require('preconditions').singleton(); var inherits = require('inherits'); var events = require('events'); +var async = require('async'); var bitcore = require('bitcore'); var bignum = bitcore.Bignum; @@ -865,22 +866,22 @@ Wallet.prototype._lockIncomming = function() { Wallet.prototype._setBlockchainListeners = function() { var self = this; - this.blockchain.removeAllListeners(); + self.blockchain.removeAllListeners(); - this.blockchain.on('reconnect', function(attempts) { - log.debug('Wallet:' + this.id +'blockchain reconnect event'); + self.blockchain.on('reconnect', function(attempts) { + log.debug('Wallet:' + self.id +'blockchain reconnect event'); self.emit('insightReconnected'); // Subscription should persist? TODO //self.subscribeToAddresses(); }); - this.blockchain.on('disconnect', function() { - log.debug('Wallet:' + this.id +'blockchain disconnect event'); + self.blockchain.on('disconnect', function() { + log.debug('Wallet:' + self.id +'blockchain disconnect event'); self.emit('insightError'); }); - this.blockchain.on('tx', function(tx) { - log.debug('Wallet:' + this.id +'blockchain tx event'); + self.blockchain.on('tx', function(tx) { + log.debug('Wallet:' + self.id +'blockchain tx event'); var addresses = self.getAddressesInfo(); var addr = _.findWhere(addresses, { addressStr: tx.address diff --git a/test/Wallet.js b/test/Wallet.js index f344969b4..5756a5fe8 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -419,20 +419,15 @@ describe('Wallet model', function() { it('decodeSecret check', function() { - (function() { - Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM'); - }).should.not. - throw(); + var s = Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM'); + should.exist(s); - (function() { - Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK'); - }).should. - throw(); + s= Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK'); + s.should.equal(false); - (function() { - Wallet.decodeSecret('12345'); - }).should. - throw(); + + s= Wallet.decodeSecret('123456'); + s.should.equal(false); }); diff --git a/test/models/Identity.js b/test/models/Identity.js index 3544e8899..40c22e813 100644 --- a/test/models/Identity.js +++ b/test/models/Identity.js @@ -42,6 +42,7 @@ describe('Identity model', function() { wallet = sinon.stub(); wallet.store = sinon.stub().yields(null); + wallet.netStart = sinon.stub(); wallet.getId = sinon.stub().returns('wid:123'); Identity._newWallet = sinon.stub().returns(wallet); @@ -129,7 +130,7 @@ describe('Identity model', function() { storage.getFirst = sinon.stub().yields('wallet1234'); profile.listWallets = sinon.stub().returns([{id:'walletid'}]); Identity._openProfile = sinon.stub().callsArgWith(3, null, profile); - Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); + Identity._walletRead = sinon.stub().callsArgWith(2, null, wallet); }); it('should call ._openProfile', function(done) { @@ -234,7 +235,7 @@ describe('Identity model', function() { var wallet = sinon.stub(); wallet.store = sinon.stub().yields(null); - Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); + Identity._walletRead = sinon.stub().callsArgWith(2, null, wallet); }); it('should return wallet and call .store, .setLastOpenedTs & .migrateWallet', function(done) { diff --git a/views/includes/sidebar.html b/views/includes/sidebar.html index d10c0cf10..5507316b5 100644 --- a/views/includes/sidebar.html +++ b/views/includes/sidebar.html @@ -64,6 +64,10 @@ + +
  • {{'Close'|translate}} From 2c2f2c3cfc65bdf91d1beb44dff26d22fc9fb2be Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 10 Oct 2014 12:02:46 -0300 Subject: [PATCH 042/182] update models tests to new API --- js/models/Identity.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 7698d8da6..c540f2c97 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -1,18 +1,18 @@ 'use strict'; var preconditions = require('preconditions').singleton(); +var _ = require('underscore'); +var log = require('../log'); + +var version = require('../../version').version; var TxProposals = require('./TxProposals'); var PublicKeyRing = require('./PublicKeyRing'); var PrivateKey = require('./PrivateKey'); var Wallet = require('./Wallet'); -var _ = require('underscore'); -var log = require('../log'); -var version = require('../../version').version; var PluginManager = require('./PluginManager'); var Profile = require('./Profile'); var Insight = module.exports.Insight = require('./Insight'); var Async = module.exports.Async = require('./Async'); -var preconditions = require('preconditions').singleton(); var Storage = module.exports.Storage = require('./Storage'); /** From 572dff6baffc10e67ac2de4516e0371676b6e5b2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 10 Oct 2014 17:10:06 -0300 Subject: [PATCH 043/182] rm WalletFactory tests --- test/WalletFactory.js | 603 ------------------------------------------ 1 file changed, 603 deletions(-) delete mode 100644 test/WalletFactory.js diff --git a/test/WalletFactory.js b/test/WalletFactory.js deleted file mode 100644 index 0bd5a71c5..000000000 --- a/test/WalletFactory.js +++ /dev/null @@ -1,603 +0,0 @@ -'use strict'; - - -var FakeNetwork = requireMock('FakeNetwork'); -var FakeBlockchain = requireMock('FakeBlockchain'); -var FakeStorage = function FakeStorage() {}; -var WalletFactory = copay.WalletFactory; -var Passphrase = copay.Passphrase; -var mockLocalStorage = requireMock('FakeLocalStorage'); -var mockSessionStorage = requireMock('FakeLocalStorage'); - - -var PERSISTED_PROPERTIES = copay.Wallet.PERSISTED_PROPERTIES; - -function assertObjectEqual(a, b) { - PERSISTED_PROPERTIES.forEach(function(k) { - if (a[k] && b[k]) { - _.omit(a[k], 'name').should.be.deep.equal(b[k], k + ' differs'); - } - }) -} - - -describe('WalletFactory model', function() { - - var wf; - - beforeEach(function() { - wf = new WalletFactory(config, '0.0.1'); - - wf.storage.setPassphrase = sinon.spy(); - wf.storage.getSessionId = sinon.spy(); - wf.storage.setFromObj = sinon.spy(); - wf.storage.setLastOpened = sinon.stub().yields(null); - - - var w = sinon.stub(); - w.store = sinon.stub().yields(null); - - wf._getWallet = sinon.stub().returns(w); - }); - - - afterEach(function() { - wf = undefined; - }); - - - - var config = { - Network: FakeNetwork, - Blockchain: FakeBlockchain, - Storage: FakeStorage, - wallet: { - requiredCopayers: 3, - totalCopayers: 5, - spendUnconfirmed: 1, - reconnectDelay: 100, - - }, - blockchain: { - host: 'test.insight.is', - port: 80, - schema: 'https' - }, - networkName: 'testnet', - passphrase: { - iterations: 100, - storageSalt: 'mjuBtGybi/4=', - }, - - // network layer config - network: { - testnet: { - url: 'https://test-insight.bitpay.com:443' - }, - livenet: { - url: 'https://insight.bitpay.com:443' - }, - }, - - }; - - describe('#constructor', function() { - it('should create the factory', function() { - var wf = new WalletFactory(config, '0.0.1'); - should.exist(wf); - wf.walletDefaults.should.deep.equal(config.wallet); - wf.version.should.equal('0.0.1'); - }); - }); - - // TODO this is a WALLET TEST! not Wallet Factory. Move it. - describe('#fromObj / #toObj', function() { - it('round trip', function() { - var wf = new WalletFactory(config, '0.0.5'); - var original = JSON.parse(o); - var o2 = wf.fromObj(original).toObj(); - assertObjectEqual(o2, original); - }); - - it('round trip, using old copayerIndex', function() { - var wf = new WalletFactory(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(o)); - - should.exist(w); - w.id.should.equal("dbfe10c3fae71cea"); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj()); - should.exist(w.privateKey.toObj()); - assertObjectEqual(w.toObj(), JSON.parse(o)); - }); - - it('#fromObj, skipping fields', function() { - var wf = new WalletFactory(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(o), ['publicKeyRing']); - - should.exist(w); - w.id.should.equal("dbfe10c3fae71cea"); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj()); - should.exist(w.privateKey.toObj()); - (function() { - assertObjectEqual(w.toObj(), JSON.parse(o)) - }).should.throw(); - }); - - it('support old index schema: #fromObj #toObj round trip', function() { - var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; - var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; - - var wf = new WalletFactory(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(o)); - - should.exist(w); - w.id.should.equal("dbfe10c3fae71cea"); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj); - should.exist(w.privateKey.toObj); - - assertObjectEqual(w.toObj(), JSON.parse(o2)); - }); - }); - - describe('#fromEncryptedObj', function() { - it('should create wallet from encrypted object', function() { - wf.storage.setPassphrase = sinon.spy(); - wf.storage.import = sinon.stub().withArgs('base64').returns('walletObj'); - wf.fromObj = sinon.stub().withArgs('walletObj').returns('ok'); - - var w = wf.fromEncryptedObj("encrypted object", "123"); - - w.should.equal('ok'); - wf.storage.setPassphrase.calledOnce.should.be.true; - wf.storage.setPassphrase.getCall(0).args[0].should.equal('123'); - wf.storage.import.calledOnce.should.be.true; - wf.fromObj.calledWith('walletObj').should.be.true; - }); - }); - - describe('#import', function() { - it('should import and update indexes', function() { - var wallet = { - id: "fake wallet", - updateIndexes: function(cb) { - cb(); - } - }; - wf.fromEncryptedObj = sinon.stub().returns(wallet); - - var w = wf.import("encrypted", "password"); - - should.exist(w); - wallet.should.equal(w); - }); - it('should import with a wrong password', function() { - wf.fromEncryptedObj = sinon.stub().returns(null); - var w = wf.import("encrypted", "passwordasdfasdf"); - should.not.exist(w); - }); - }); - - describe('#getWallets', function() { - it('should return empty array if no wallets', function(done) { - wf.storage.getWallets = sinon.stub().yields([]); - wf.storage.getLastOpened = sinon.stub().yields(null); - - wf.getWallets(function(err, ws) { - should.not.exist(err); - ws.should.deep.equal([]); - done(); - }); - }); - - it('should be able to get current wallets', function(done) { - wf.storage.getWallets = sinon.stub().yields([{ - name: 'w1', - id: 'id1', - }, { - name: 'w', - id: 'id2', - }]); - wf.storage.getLastOpened = sinon.stub().yields(null); - - wf.getWallets(function(err, ws) { - should.not.exist(err); - ws.should.deep.equal([{ - name: 'w1', - id: 'id1', - show: 'w1 ' - }, { - name: 'w', - id: 'id2', - show: 'w ' - }]); - done(); - }); - }); - it('should include last used info', function(done) { - wf.storage.getWallets = sinon.stub().yields([{ - name: 'w1', - id: 'id1', - }, { - name: 'w', - id: 'id2', - }]); - wf.storage.getLastOpened = sinon.stub().yields('id2'); - - wf.getWallets(function(err, ws) { - should.not.exist(err); - ws.should.deep.equal([{ - name: 'w1', - id: 'id1', - show: 'w1 ' - }, { - name: 'w', - id: 'id2', - lastOpened: true, - show: 'w ' - }]); - done(); - }); - }); - }); - - describe('#delete', function() { - it('should call deleteWallet', function(done) { - wf.storage.deleteWallet = sinon.stub().yields(null); - wf.delete('xxx', function() { - wf.storage.deleteWallet.getCall(0).args[0].should.equal('xxx'); - done(); - }); - }); - - it('should call lastOpened', function(done) { - wf.storage.deleteWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - wf.delete('xxx', function() { - wf.storage.setLastOpened.calledOnce.should.equal(true); - should.not.exist(wf.storage.setLastOpened.getCall(0).args[0]); - done(); - }); - }); - }); - - - describe('#read', function() { - it('should fail to read unexisting wallet', function(done) { - wf.storage.readWallet = sinon.stub().yields(null, {}); - - wf.read('id', [], function(err, w) { - should.not.exist(w); - should.exist(err); - should.exist(err.message); - var m = err.message.toString(); - m.should.to.have.string('Wallet not found'); - done(); - }); - }); - it('should fail to read broken wallet', function(done) { - wf.storage.readWallet = sinon.stub().yields(null, { - 'opts': 1 - }); - wf.read('id', [], function(err, w) { - should.not.exist(w); - should.exist(err); - should.exist(err.message); - var m = err.message.toString(); - m.should.to.have.string('Could not read'); - done(); - }); - }); - it('should read existing wallet', function(done) { - var wf = new WalletFactory(config, '0.0.1'); - wf.storage.readWallet = sinon.stub().yields(null, { - 'opts': 1 - }); - wf.fromObj = sinon.stub().returns('ok'); - wf.read('id', [], function(err, w) { - should.not.exist(err); - should.exist(w); - done(); - }); - }); - }); - - - describe('#open', function() { - var opts = { - 'requiredcopayers': 2, - 'totalcopayers': 3 - }; - - it('should call setPassphrase', function(done) { - var wf = new WalletFactory(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - - wf.open('dummy', 'xxx', function(err, w) { - wf.storage.setPassphrase.calledOnce.should.equal(true); - wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); - done(); - }); - }); - - it('should call return wallet', function(done) { - var wf = new WalletFactory(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - - wf.open('dummy', 'xxx', function(err, w) { - w.should.equal(s1); - s1.store.calledOnce.should.equal(true); - done(); - }); - }); - - - it('should call #store', function(done) { - var wf = new WalletFactory(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - - wf.open('dummy', 'xxx', function(err, w) { - s1.store.calledOnce.should.equal(true); - done(); - }); - }); - - it('should call #setLastOpened', function(done) { - var wf = new WalletFactory(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - - wf.open('dummy', 'xxx', function(err, w) { - wf.storage.setLastOpened.calledOnce.should.equal(true); - wf.storage.setLastOpened.getCall(0).args[0].should.equal('dummy'); - done(); - }); - }); - it('should call #migrateWallet', function(done) { - var wf = new WalletFactory(config, '0.0.1'); - wf.storage.setPassphrase = sinon.spy(); - - var s1 = sinon.stub(); - s1.store = sinon.stub().yields(null); - wf.read = sinon.stub().yields(null, s1); - wf.migrateWallet = sinon.stub().yields(null); - wf.storage.deleteWallet_Old = sinon.stub().yields(null); - wf.storage.removeGlobal = sinon.stub().yields(null); - wf.storage.setLastOpened = sinon.stub().yields(null); - - wf.open('dummy', 'xxx', function(err, w) { - wf.migrateWallet.calledOnce.should.equal(true); - wf.migrateWallet.getCall(0).args[0].should.equal('dummy'); - done(); - }); - }); - }); - - describe('#create', function() { - it('should create wallet', function(done) { - wf.create(null, function(err, w) { - should.exist(w); - should.not.exist(err); - done(); - }); - }); - - it('should be able to create wallets with given pk', function(done) { - var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; - wf.create({ - privateKeyHex: priv, - }, function(err, w) { - wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.equal(priv); - should.not.exist(err); - done(); - }); - }); - - it('should be able to create wallets with random pk', function(done) { - wf.create(null, function(err, w1) { - wf.create(null, function(err, w2) { - wf._getWallet.getCall(0).args[0].privateKey.toObj().extendedPrivateKeyString.should.not.equal( - wf._getWallet.getCall(1).args[0].privateKey.toObj().extendedPrivateKeyString - ); - done(); - }); - }); - }); - }); - - describe('#joinCreateSession', function() { - var opts = { - secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', - nickname: 'test', - passphrase: 'pass' - }; - - it('should yield bad network error', function(done) { - var net = wf.networks['testnet']; - net.greet = sinon.stub(); - net.on = sinon.stub(); - net.on.withArgs('data').yields('senderId', { - type: 'walletId', - networkName: 'aWeirdNetworkName', - opts: {}, - }); - opts.privHex = undefined; - wf.joinCreateSession(opts, function(err, w) { - err.should.equal('badNetwork'); - done(); - }); - }); - - - it('should yield to join error', function(done) { - opts.privHex = undefined; - var net = wf.networks['testnet']; - net.greet = sinon.stub(); - net.on = sinon.stub(); - net.on.withArgs('serverError').yields(null); - net.on.withArgs('data').yields('senderId', { - type: 'walletId', - networkName: wf.networkName, - }); - wf.joinCreateSession(opts, function(err, w) { - err.should.equal('joinError'); - done(); - }); - }); - - - it('should call network.start / create', function(done) { - opts.privHex = undefined; - var net = wf.networks['testnet']; - net.cleanUp = sinon.spy(); - net.greet = sinon.spy(); - net.start = sinon.stub().yields(null); - - net.on = sinon.stub(); - net.on.withArgs('connected').yields(null); - net.on.withArgs('data').yields('senderId', { - type: 'walletId', - networkName: 'testnet', - opts: {}, - }); - - var w = sinon.stub(); - w.sendWalletReady = sinon.spy(); - wf.create = sinon.stub().yields(null, w); - wf.joinCreateSession(opts, function(err, w) { - net.start.calledOnce.should.equal(true); - wf.create.calledOnce.should.equal(true); - wf.create.calledOnce.should.equal(true); - - w.sendWalletReady.calledOnce.should.equal(true); - w.sendWalletReady.getCall(0).args[0].should.equal('03ddbc4711534bc62ccf576ab05f2a0afd11f9e2f4016781f3f5a88de9543a229a'); - done(); - }); - }); - - it('should return walletFull', function(done) { - opts.privHex = undefined; - var net = wf.networks['testnet']; - net.cleanUp = sinon.spy(); - net.greet = sinon.spy(); - net.start = sinon.stub().yields(null); - - net.on = sinon.stub(); - net.on.withArgs('connected').yields(null); - net.on.withArgs('data').yields('senderId', { - type: 'walletId', - networkName: 'testnet', - opts: {}, - }); - wf.create = sinon.stub().yields(null, null); - wf.joinCreateSession(opts, function(err, w) { - err.should.equal('walletFull'); - done(); - }); - }); - - it('should accept a priv key a input', function() { - var wf = new WalletFactory(config, '0.0.1'); - opts.privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd'; - var net = wf.networks['testnet']; - net.cleanUp = sinon.spy(); - net.start = sinon.spy(); - wf.joinCreateSession(opts, function(err, w) { - net.start.getCall(0).args[0].privkey.should.equal('ddc2fa8c583a73c4b2a24630ec7c283df4e7c230a02c4e48bc36ec61687afd7d'); - }); - }); - it('should call network.start with private key', function() { - opts.privHex = undefined; - var wf = new WalletFactory(config, '0.0.1'); - var net = wf.networks['testnet']; - net.cleanUp = sinon.spy(); - net.start = sinon.spy(); - wf.joinCreateSession(opts, function(err, w) { - net.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer - }); - }); - }); - - - describe('Backwards compatibility tests', function() { - it('should be able to import unencrypted legacy wallet TxProposal: v0', function() { - var wf = new WalletFactory(config, '0.0.5'); - var w = wf.fromObj(JSON.parse(legacyO)); - - should.exist(w); - w.id.should.equal('55d4bd062d32f90a'); - should.exist(w.publicKeyRing.getCopayerId); - should.exist(w.txProposals.toObj()); - should.exist(w.privateKey.toObj()); - }); - - it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function() { - - wf.storage.import = sinon.stub(); - wf.storage.setPassphrase = sinon.spy(); - wf.storage.import.withArgs('dummy').returns(JSON.parse(legacy1)); - - var w = wf.import('dummy', 'xxx'); - should.exist(w); - wf.storage.setPassphrase.calledOnce.should.equal(true); - wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); - - w.isReady().should.equal(true); - var wo = w.toObj(); - wo.opts.id.should.equal('48ba2f1ffdfe9708'); - wo.opts.spendUnconfirmed.should.equal(true); - wo.opts.requiredCopayers.should.equal(1); - wo.opts.totalCopayers.should.equal(1); - wo.opts.name.should.equal('pepe wallet'); - wo.opts.version.should.equal('0.4.7'); - wo.publicKeyRing.walletId.should.equal('48ba2f1ffdfe9708'); - wo.publicKeyRing.networkName.should.equal('testnet'); - wo.publicKeyRing.requiredCopayers.should.equal(1); - wo.publicKeyRing.totalCopayers.should.equal(1); - wo.publicKeyRing.indexes.length.should.equal(2); - JSON.stringify(wo.publicKeyRing.indexes[0]).should.equal('{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1}'); - JSON.stringify(wo.publicKeyRing.indexes[1]).should.equal('{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}'); - wo.publicKeyRing.copayersBackup.length.should.equal(1); - wo.publicKeyRing.copayersBackup[0].should.equal('0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f'); - wo.publicKeyRing.copayersExtPubKeys.length.should.equal(1); - wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6'); - wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm'); - wo.privateKey.networkName.should.equal('testnet'); - - }); - }); -}); - - -var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; - -var legacyO = '{"opts":{"id":"55d4bd062d32f90a","spendUnconfirmed":true,"requiredCopayers":2,"totalCopayers":2,"name":"xcvzxcv","version":"0.3.2"},"networkNonce":"53d25e8600000009","networkNonces":[],"publicKeyRing":{"walletId":"55d4bd062d32f90a","networkName":"testnet","requiredCopayers":2,"totalCopayers":2,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":4,"receiveIndex":2},{"copayerIndex":1,"changeIndex":5,"receiveIndex":2}],"copayersBackup":["02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5"],"copayersExtPubKeys":["tpubD94LTzAUiW99mpA59nyf6fAHh4xKGmnwbgCV4gU2bRpeN9CRiMSurqme22px5NmJAo6FdcdH883Zu98VbqyhesCJ86kUEjH3Zpufy5FfcaC","tpubDA2U9H6LkRHDRbRxHBp4VTbxPc7JqsvtcLxrE5QJF8z1iT6hMJ1pXSVf57GWRcxXutYvpoXRurDVGsscJauMtnJBkYAWBVExYmm91XQE2zz"],"nicknameFor":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":"asdf","02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":"qwerqw"},"publicKeysCache":{"m/0/0/0":["028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90","0332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec8"],"m/1/0/0":["0220ad514cf593d0c3905d3bb49bc5767a9410823bf9b77ea5ef2cf1d1016d77a8","02fd42cf66f1dbdc7bbb9ae09aecea72df479ffe5a0c4641301067e331d12e416d"],"m/1/0/1":["0315f7868eaf1f9b7127e3f7e0222c5e473eea003e34700f4758b6873c525d6723","02a2e8ed5e90dd39e3842fc790e06178997dbca319987f365317589e2a71a93658"],"m/0/1/0":["0244a25a0b97b26707fd855c15b046b901be85a3b70a781d0678608e633440eeca","0358cdcbc528ddfb7173b0dab283f702be82546ff031e4a832a7270080cb875959"],"m/0/1/1":["025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f","020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d"],"m/1/1/0":["02fd0e7c62b7b58d1ea7bb4cb84d53b019df99d3703a42aed73a2cfa15f3af5d08","0355a15912e76072ef50e6643376b8a9da8422ed4f8ea07b1d84d4989be5a39b2e"],"m/1/1/1":["03bc3e1f4db32efd8eb1fd44a1665938d59628429c67e1e8b7054ab5717f4e6750","03c4c817b633ac31f44f16f390af831d35f7d98744a52a0f23e9598967342255f8"],"m/1/1/2":["02826fe7e9da408480ddeb1d4414c5100b350f862ca718e27122681e1a0ca35077","02bd25af907bb3edbf6b2cd1ea90eaa92cc93ec47bea7d339af44c1d2c05708e99"],"m/0/1/2":["0337a1a70364b94745d6e26d2d28919cf528304f52765f12ef43e3d6da0a6c8dc0","039d83db9aa43e6e00e0304e6971b6079d79dc12d8d55ce2e6fc24a52ba8d41329"],"m/0/0/1":["0359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b8138","037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d93"],"m/1/1/3":["02600e5c41670773a213a4cb58c8f2fa3e83840784bc7f0b56925e1075e06632c2","036d01867af5f61371151ef7d9026fa0400a623f6924e404ee0b856625268972f9"],"m/0/1/3":["03e5a9b039b187ca8e065627df402e4a5b196b94198542da7036879de08be63d2d","0304f3e0b70f696d80e5785dc7747d6dcb55ba24c31f2d80bf184b4e582e6b47fc"],"m/1/1/4":["03741afa5bd50d6ba5801064c810fae84f6a4557d6a88ddc8591d0d4eb68a8fc41","0214dd6ce6073b05999fb887098ca6f7e1d0b4fdc0760557786907df353df90d1c"],"m/2147483647/1/0":["033e072a53ea835763a03c66e35c35384736210a1bb7d7ee6d9a3e109e82426b30","02e37b5570c053da8a8ee587be86fc629775c4db890aba2745ccc4e4dcc8c31041"],"m/2147483647/1/1":["0228a6de42ef421c263d1efd9f28d9a7d15a261995028a24eff6b9f1c3fc46e6bf","0226cff885cb0d607cc9cf69a7608316eb3fb2ec344c0c9956246ba776116fc396"],"m/2147483647/1/2":["034fe2a8f0b98445eb5810fe36572ad2f64ed9bf64dc9de624f99c0142cb07c682","02f2c5c758e32293f5c193fd69afadbba83abafb397db01e6f2b447690e900475a"],"m/2147483647/1/3":["02b25ef9434446c51f10678f787e4913de582e34d164bd3b06af7732c5476df1a8","025d51a1efd59bcff22ee2e0af61b21a7ba5f639e20dfdf25690e926005177dd0c"],"m/2147483647/1/4":["03e5734e1d29b2f684d0446b7a2ffbd0ba8952570a502d0d14b1efd8f24b61be53","0258fc28a324848d8d0154e8614815e35c668d274a8f01957bb99aab8dc8f386c0"],"m/2147483647/1/5":["021f9e775246765e1cfba0ae453b4eae6cd4ae5a57a09c319edbe89d4dbbf23be3","02857f66571a1c3eb9e72d22ae88e734c03d448bced4dcfd345c2059468124c741"],"m/2147483647/1/6":["02c072f329391a25255dc6452e5f5220966869dbf736ba8a8c3ae9d273a84bc3fd","030920a8b8e88c4db2871a7df0878a86cf0695f6d96bb50c701c3454f3df25176a"],"m/2147483647/1/7":["036bf329fc19bce10cf1999fae5bfa80290ff7b44776b49c7b0dc9eec6cffcfa21","03955a549875b4f7b9be28b9ff4bcd51ad2bc224430b1634baef890585885d5e1b"],"m/2147483647/1/8":["024879c9c9a261b3141ecfa1c79c4efc25278c844ecd1dcfcb95d9c19581fbdd25","03fb4a5fdb91239df3ccf7f61a5b99e7e72483101e21c9d1ee0d85544e9354c6c7"],"m/2147483647/1/9":["035928a107ec01f78cd586914d5a49710fd42e352b1312e3ad0eeb2c9666fdf8e7","03a54c03093797854829c75357f092356352a109042bbb83bdac20cb4e5eca27ea"],"m/2147483647/1/10":["021e7a3a7efe888c5e820b5cf0f03317b2b4bf438d8563449aeb7a77cade97f136","03ec0960b3d1df52ca3cc2c82b7d97063400da4dd051bba2f9bab6cb44aee01efa"],"m/2147483647/1/11":["035d70c26b7f429861f555f7c0d99947411b23b7f95303fb8d5de5b82a95aa30fc","038b922f7024f5446d6b48e5253643543b35c006d90fd37688105c6cefcd8adb8a"],"m/2147483647/1/12":["02158d6503891c6c65a606221dbf5c68d0832288975914007968419939588ecb24","0248264cb1763a3f4de9b34787b4bc5443ec92ef915927494bb9f1c1c0b498c7ca"],"m/2147483647/1/13":["0349965eea38a25ae0c061faeac4c4e57e648bc4c0f059d07b3b8b7962cbc0dde5","0352243d9269565ce2a1ffdd0b8e43a442c6dd1c9edda86eaaf2cba5a4a95c40f1"],"m/2147483647/1/14":["030fa6e3d0c5cedc0581955395c77cbe134c912a47971023b9695332df3f7bb200","03f2cf09e33326fb59bf3f13e6298d2d5d29c9eae3b872e5a851e8d8d77259c883"],"m/2147483647/1/15":["02bf0d45e41339f552df6f8baf4392142921fd38b0f2a4388a905ff6cbacbc278a","03fabe46bb6706a1b8edfd28c046a8891b4530bbe5305080b72b0d08ebdf7b8c0a"],"m/2147483647/1/16":["03a4e3146ed34d6a8af4e4379e6edcff32cb0373ba232b3d746af3052f674133ac","030311b73c6f5c46ddffc0cfce6e5ed0b671d94267d8e52cd8837f2a479916eb91"],"m/2147483647/1/17":["03233df93c762d2f06c7f5f388e4e0a8dbdb13302acba0d2d6995c487d8aec9f2f","024badfdcb7e772ac7fc1c46d3943b07500edbbece105cdeff3eb9e9fcc9f54782"],"m/2147483647/1/18":["0364035475a098e00eb010c500cad3c90af3e81a4bd613144bc9433a150f14718b","028223dc8142154e7477ce000b3dc13e1d15a901553d9b18864c8645b582b38fe6"],"m/2147483647/1/19":["03971b74b4ac4bdaadf636baa4caa82fe5355471ed6ea05a9cbe5fc6c9e4b9db76","0202ebffacd01f83849e5bc5c0e2c317bc5fb2fbcb2d6d4482a5235f9f1308b61a"],"m/0/1/4":["03005ee9ff028c98fd132e531023f2f2b61ff0d26022f979dd98088d2ba167b031","0345ea82e8dfe38277f0c3aee18d2dd93edb63e8663ac83328a7934d2ca57006f6"],"m/0/1/5":["0391bc4990b71d8a3f156ae7107929ed6372b0b4ba8a868253f71ba7189d1efa02","0312a74cf2e7c0dd41897d04fabfd8cc3187b84a28305cfc79315b24e6fe23a6b4"],"m/0/1/6":["021a38c492607ff9684a4fec445e47b5b7100d3ef9e9dc0d0b37c0a646d28d4f77","03ae0b46ab36f97447ebaa53f2b5c8f090f15395378785f2fd285eeba17fbf3f65"],"m/0/1/7":["0308cdec88c1ffe16edc98853d9c08dbd4ba2541ba566668ca17bda19d7eb3481f","02dd622267c2e68287287b8b61724f76fbe84096a56aa5054af92f8fe25380e2d1"],"m/0/1/8":["039647da9ad725836bcb28a3e0497659a28d7749d1416c421a0a01c62d237ee962","022e22aa61eafda0dd8820427f1a06314d352a15ea8645e7ab9b80920017084d82"],"m/0/1/9":["03a4ade946076c6962b70c70ac7fad3a87efb59a1d0a4e32bda13a6d47fe9df961","029a07235aba04ab69526e117d836d5b3fae5cfc8c5e72b10c6d1afd261ccc19f3"],"m/0/1/10":["03c78e9b6493b22790db1acea20df9444e0f9c424fc5756e7a32c290ae01783953","0254c130ee467a96570c9f5ebea89de04f0b1db1686b164f2694339bef8f25dd88"],"m/0/1/11":["03a762c43318ef8d4840fab04c8db73797dc648825fac60f2730b4c76678df1cf3","0212c684a4de8e750ad2dfe2b136370ab9803eca178ed9a27b3990c29b067de35c"],"m/0/1/12":["02702d221f9b15c5cf75ac2f497a6c63e60213087c3d2d3be46768e3ebd238e26e","03ed58580744deb357258e44548212038670769d8d51e385d4fb8414311fd01b52"],"m/0/1/13":["0320e0597b54c62768352f433389cee4725d6094d7bcb5c72265edcc0933829aff","02c5706f11b9a85f3176c572842b7c9812c2195058d24d945bc026b00312740e76"],"m/0/1/14":["02fe43077676b844226d3aaa62e8a86d237710d92f882366944acbde0c8992fcaf","039a6a8662abb8910741cf331320549665e9feb28ca94d1ab6a43c84fa330b94ee"],"m/0/1/15":["0369f99f72847af93d50ab8ee75b6e7e912d26e27be96f6d6b7215cf7daeff7ba5","02521700cc07c953ba5aa586fb0e4795a34dffc68c5fb43e038be3866e40f4daed"],"m/0/1/16":["02f67d1d89bd8fe2f91c5b973cbdacfb4ba440e7656bce284cf73d549625607347","035da9cfac5a803dcb2b283b02a2515a4a1bcbf3d19e0d180aee8fc30193bc0555"],"m/0/1/17":["02c024ec199d240e8d6c66276b94b91071f7cdf2bef540c29d6d18d25de7b1cf7c","02190865f9dafae3f7f05c093463be5632946422ddda0a6fef6904390792516067"],"m/0/1/18":["035ed504d7704ad984a333b8eb0fceb8be043da9284de31ed84d9e68d90c75507d","033303c415b50421732402df00f4baa219f334647a7eb5014b9f8079864d6ab558"],"m/0/1/19":["02ce49fe86b0eee73663b1ee867b16b97c876af26f12764c528a2e6d0eb55ad3d7","03ab969bc81796b88e44c340d854df955fc60ea17ea92db5d3115595d6dec890d8"],"m/0/1/20":["03e2fa915378cbdffa0d919b0fb50c7256ca731b9d571b3365e486893a1d43079c","038d058b895cf084dccfcc9367e4796a5cf4ddceed6c35f6885d75c80119613350"],"m/0/1/21":["02fcb1bf644446b5b42205272af72f0aeab9e92ca29aafa91c5fb69142764017aa","035c5fe5c8811603279a5b72b6c30735d702817db1eab937c622269e28192ffa90"],"m/0/1/22":["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5","022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"],"m/0/1/23":["03f40b82fe8cacff08879f13c45f443a3dc3ea98e1d75d5f32a19f5e5a8f7a905b","028415ee458e4dcfd440ce969726f3b58ae74fb6cf3995ced099579211e7419844"],"m/1/1/5":["032748a6282e21f571b8c8dd49e775deb83c90fcf88dc4ba81d878536973709c3f","020837cd68f14ce571b335eecd1b6fa0af43e1576dd9721aaca2a8ab639ac6b7cd"],"m/1/1/6":["0337032efb013dc92bb8dccfbdda9f5c28f0039a9c60953d41003d095e9f9778af","03ceed2da6b9603297061dc8eb930112ba726b2ccf5eec67f4866a05ca4049a22b"],"m/1/1/7":["0383c96ac2af7d203f69133b2fab6b68366b5075ad6957fa06759df3b20fbfec70","0311385f79834cedaf2230a48c0f9dc8e794da1869fc595db2518d62debb85579a"],"m/1/1/8":["03efc649680280f4e4df96da923bc88330275004125ebe5483c2f3e05ca52e19a4","02803c02d197d780388259afbd001ae41fa3eb3e2bac9627aff540521c184c3b23"],"m/1/1/9":["03af2fe6aa027a76b42c1c4050a040bfd026ad2daec1bb96a5fe2d026a7df919de","02ce14163047c640228796fb1f72bbe3afb05819ad141598a4f021058a6f79dd3b"],"m/1/1/10":["033770378bd762cf0408e44e4e604bef77e336170428c506949b1a4f1f2963e574","02c58ed43946f699dbd3e36d3e9aab2714cadeb19ecd3a56e4328c50336b4a76cb"],"m/1/1/11":["02898a1545fa19bdca92adc498698d27b86529cd4c08946d9d29604734b86f31af","02b402767a045ede072600924401c0d720000b2ed59fa444bfdbef4a5f1cead745"],"m/1/1/12":["039b8659430be49913e2cd869aa8c99ccf49a13df35837370b792033dadb891483","03264e63df292257cc76babb15d15bef620d1c2f8c3bbc78d6ea02d127e5ee7386"],"m/1/1/13":["02381a559791b8e86bf546e2c718ae63cf24eed0518a58e4d4a4b310adf2cd38fa","02d7f8283a4418d912508901b4a3db0d2103206dfdd74b3c75648671e20ecfd445"],"m/1/1/14":["020376e8c550b7d9faa0b2da947a2a36fab22c6e8190b6f99460b6022017bb97d7","03fbc5299190e6628de28c92aaa12e3a131b21eb7266462c46fbedeb86fa878055"],"m/1/1/15":["027209fd3b0cf7368180a5dbb16b928c997d33fccb78505d48440c7d23eadf5460","03450bfb22858726cd7e228e6733f69457546978a95188565c53e0d1c0d6070ea8"],"m/1/1/16":["03cb355ba04f64293793855121bab5831f84a3a3edf7cd31fccaa6d67c407a4912","028bc897a39c1224610b765a80f4cd8ab79cb37776f58fec9c10ac6f649d1f3c72"],"m/1/1/17":["03f4cb0564d7e2c6b85673503b7954db22779f29a8f3374904573984e318a96bf1","037c11b6ee906d84aa7eed359d758d986d912b6f8e5cbb1acf0982a77b3ef812c4"],"m/1/1/18":["02d2e5798f33f6889472857744316f2d253f25f88379610063f40cfe5798d9858f","0253cefdfe9ca987cbf1c950b6246d5b7a194d8dfad47c3a78dbbc5c1d01511d97"],"m/1/1/19":["0336c325f5aed366ffc10d553f2bfd4d69e66cbe1688d77af14efc8827aea2e318","0378b1b9a6074f9f2ab4fa9ad1e14649c621b0c8124a1b148914d3c10e6ab390c6"],"m/1/1/20":["03ea55740a734689ce778a8c00df8ebf4274c8f66de7d05646fe5c927773ff7f2e","02275b558d49aef955b6dee51a3c0a53f4b076b97bb3f26abcc82540168ec87cac"],"m/1/1/21":["03c77869c9984664eac9c238f4b6d806c9f48ca8a736c48450f398834db2aa915c","02d984f548c7f60c09dad3287cfc48807bc8157123989636c713be61be6a2e9ced"],"m/1/1/22":["03ed7c6a3c854c1f9459891691cc32671402f9e47126919878251e568dbdf353f8","02a113dab22cd9e46967b3fd76b9b9ec1d227d88817a9300e42d332cca2a0877fd"],"m/1/1/23":["02ee186432dcf69fda50a6fdbd94651817d8a271c273a5b70cab3ec4ae77a3753b","02291370aad9de0dac676355ced64e268b0c431a51f42f12d13f5144940fce4285"],"m/1/1/24":["02bf71435e84e66547c8c583d5ba226a5ac4d935e0a9f9603ecd8925c3e847e91a","03578d8657d285a89d9d597632db662cfef9baccfb55c76b1e87948a94fc9de30d"],"m/2147483647/0/0":["02a8425bbe23426219065969f695a6c3e242b24e57226bffdd542be8fd6be968c9","03057a42fdb6569fb1615b173ccb702453db2eac5be4291b82d4511461eafbed87"],"m/2147483647/0/1":["0250c3d3e86e332010c5233c2ec3bc728026002f0037cb3382d6318409b0e70796","02cfac1e7c4c88191201080f8316af52d9faa6ba624a6e160279e9fac4d1cf79a9"],"m/2147483647/0/2":["02a8c266a5b92eb50c8be91f95e4d1ad968b2f57d527377fd642d63fb84474f61a","028cc954ab31bd179ff80b8a05f95430ae534e61b3ff35f5284fa2fbe1832ceccb"],"m/2147483647/0/3":["02f719e1a7ab00ea98611453fb03d44c1da04655bed74af392534d70099039b4c2","03bfa548bfd4718c50bfce173f780eadcfb679d9c0206c91a2fa1879a9cf7558b2"],"m/2147483647/0/4":["0362c0695d397ca26bf47f0e641bb3cfb06ff29ccac2e1d56ded3afcf88b1e688d","02f9d87b05bdb3b9e82f506b43f813041c0e403274adc23d11e5e1651e34b606c2"],"m/2147483647/0/5":["033731323032d4ee08e858fc71f93970444333e183a1d5052e1d08cfb511e262c8","023e12556cef67ade35b7758916b5e1a3ebe074ccd35c5d8eff6b01321f63eb495"],"m/2147483647/0/6":["025d11b90081972bc1c258c9d6f476dfc2f95b69f0e9935322bf9c21deb580ff64","02b065f56a378907354f0738a0ed74f10660c6b5dd68c9f992093b75ce3d7d8b72"],"m/2147483647/0/7":["0210e721e8a35db9d8c855a0d346f60c09208f3be80b39e03af2c29db777332c71","0277f352969fadb1f1835f9a0fa99c6a3c7b6c281be5b2794c88a708eb177ea33d"],"m/2147483647/0/8":["02998d8d41e4215cd2a961a415a3ed0b1f984f1627719a7b102a75864943c4d87b","03d8ed7fc8f68a77f68d3afd007b7aa4c89944195143630ce183f0fa5438f2b559"],"m/2147483647/0/9":["0324fa91737588e4f85937303ce65c3b91b5f2ae506a72d92b83e3f5f9aeeb3c6f","02a011be72c4a400319212228106af278823a97acfe0a67e1ecd866d446b315114"],"m/2147483647/0/10":["025886ba287922a904881c7315e6fcc410a7976741771a5937d3a1a01b529f21fd","0243bb91ceed9d29d0c2ca66a8ab77e82110bbcc023beb4106f787964f44a0b972"],"m/2147483647/0/11":["0369d21684894cc2d4b2f5e581ede3cac9e8db4161a08e7737c1be129bb673d3d5","03c9ef27e3cd3dadc078fdfd9936a7ad9bf7954747085cf8f8a2a5bb3431f68a9f"],"m/2147483647/0/12":["03a73b8fd859bf6acebffdfffa2597199091daedd2c011ac67fc3494d8a1a8ceb6","025a213f7771c8be03f43f2e7f469ad4ef2cf6907ea284b227a786d1f55dfa7144"],"m/2147483647/0/13":["03a09f7ca257e1ab263cd5e6b0addc3ff868b93df132321d98775ca3505efb576f","03454c715739164bc55f347a651439cdf3ec146b35d2927beb60e8290b3916e082"],"m/2147483647/0/14":["03a64b1f7bd94a6b1a6e84ea444e0ba04e9deb86460934ccc37c0615a134a8257b","02794f09210b1811a455f3e1c7bcd35c76dff2523190fef9615eb27e2376acac1c"],"m/2147483647/0/15":["0392dca2fd9a3bc2b2a7d90a848719069fbc5f22bff7327bb8186c032514085263","032ee8a33ea76d70c7ae839448ca6c5b1af89146f2922e23ba1822df42dbc7e66a"],"m/2147483647/0/16":["031a22a1a3c1abad7c4d782ef6ba3cc00f2e8fe549eb33e0732200aff6d3174831","03bdce9781289e0c31cf727f4c93fe46f7930dd8fd68f818ce241f1ede268e8e0e"],"m/2147483647/0/17":["03b12d27e9aea2c2ad598e54e40860a705ac2ca2427aa511b501b38ec368ea5c7d","03e60d35d84d4536cad895215256b312bb4879a8d417251c279995e58f25da3d54"],"m/2147483647/0/18":["0380266cc9a9673676ad6a1b2e7148766df9c25b4dce299e5edc4f65b72aa58e64","0329e2a8a48c06c0c45dfdd2ab33e6455551557d8ebaf8c12fdf7470f8c45f1d28"],"m/2147483647/0/19":["036fe62af85560d7eea7c7af55e60b32a97dca80134d0aedffb19eb2705b9d6e01","02381c2c30b9f81e2a53c69028fbe11803acad0420b267719b7a80870be0baaeb7"],"m/0/0/2":["027bf94b8fc4e9b42683af25fda125ccab8760040717d100270dd4afd032692daf","026382c6c9357250d96dc21e43c053857a64efeac1887fdcbc107fbe3ecfc6115a"],"m/0/0/3":["03fd203acbd9af3cbbfb709458f8952078234a36094f12d00372e4b2b14cfdf419","03f2e5db59aea5dc89f53ac2a9f4ef66d41265c45afc5d763e0ca61ab70c7c61ec"],"m/0/0/4":["02a1d7cf4fcdbbf4de4002b844c3bff1639073f1cd6e5c4a4e02596b45d3f518c2","03b5fba813294e6ae096ea158833453caa5a945609b0a554696091b9b152bb0f7d"],"m/0/0/5":["0261d37e3b56ef4e106c59753037f516a4b1c45e056b2a3e00f8b77f15aaa7f8a5","0256a55e66e0de1603f0d600c0eb5f5486cf3512a776a36f3ab0d1941fc0dc9b09"],"m/0/0/6":["031db2826af215fe6cbe3f6e121b0497840fc49be133cff0a4d4eab679d6b99d70","021dd722c3f35dd04fcdb57f09b76c723d521fb36751de03ffd08096ddf1dc1f86"],"m/0/0/7":["0354ea75bdd9eb5beae7262e4a5eeb58bd10103ee0185e85b749ea39f6615d0f62","03f2c8f3b6478c0501a8578d5caf5ac2974f8213fc5e699d62dd2af58fbe8781d4"],"m/0/0/8":["0282e67df3bcd1e1662469b4c3151fb50ee1e46b75d787d91184c16b9803131f82","02921a7054af1e425f4137a5eb6b34d1f2b9d81c2625230194bc30657bb4277e11"],"m/0/0/9":["033e7e387933983ceab37c8388bd8ebc5119760f493ffe6f083bef0e5dfe22891d","02d660d60cc55d80912e0745cb142a8596a4604fbf72f9aadec0599aa2ed62461a"],"m/0/0/10":["022ce5b2750ae34512199856eab9e912dc25281cd8b88e7688a46c3b9a389701cb","02f14aa1608fce3b6088148709eb5fe72b61699c931fa8d95a45fab1106859d1b0"],"m/0/0/11":["0288dbef3302c1bc5556028adb33e2f9e03c119dbad4f706befb8ce86cea459f2b","03f13ced465e2e0a3aaa8895f3185d5711e0bebdaf507610b7a669ac8fc82da8fe"],"m/0/0/12":["031ab4677885340d2f927ccc9747f4346b79e4eb6c750695095a8a2524610fa94a","038c881910fbd8b50d193db4e0c84f5b7840820397f92cf0718a8e06d027125503"],"m/0/0/13":["031b568452cba22eb7a88c6085489e53e35abd16068882e71a140e47e12dee9c61","020d09885ee362101d12d34ce0918d41593634db1b9413e5415c6755753b9330e8"],"m/0/0/14":["024177bc9aa03cfc72eda2dfddffd7fe9d0c2f007fc3ba1a48280feae2b9fb117a","03394ad321668440c08da76eb35475ba3a8c0e8cbe0ed81468673a8c72d38fe457"],"m/0/0/15":["02037b1cc696ffbe9eba3684edd53653386ef6cd7728401c40120037593a4c2ae2","020ab8d6900ec9c11ca5d96dfc0ce7cf0ee71653a7c45118e89abb4b113147e53a"],"m/0/0/16":["023bcbb8d4726a546087cdb83740adf0ace879b7195a572c652fa8ce4dbe195a04","0392721b230d5163d28b27fc7e059b875711f12b3da448eabe7229bde57530e637"],"m/0/0/17":["02498ee74e849d3e9261dd1863038caf83d6a3bc2eeebecf17055d4bab44dee77f","03d4dc104b2e0981693e8097437de9b05334a85e2c8edb02783897859bdbc93e32"],"m/0/0/18":["0218a9f524fe54abf8c3afd21314296cfd93eaa9227acbd457e6c9a742dc233cf4","03760f3d0c5db969bda698ff9352e3b7c332216c34825f4c6e857e39c9aee7cd35"],"m/0/0/19":["033dd51f7737f0e9db79f5c38e4298bf3396346904ef3933d290a22e5b77048d9e","0221b2eedccb9a37515263071550069b3b349a166f0f131d0028e8600d9a2251b9"],"m/0/0/20":["02cb6c39161f3244d7769f7ab96346cae2cf21cb6f4538f5e7382d363dc2f836c7","034f7bda4d1e9ed6a3774608a4d6cd8582ab59fe3187f8a7a7cf914d89426ebe28"],"m/0/0/21":["035490549d65f1360f10340037250b171470ff4c86966318a2b1eead6d8b969aea","03f6a04f6fcd07a4f32c82d53710ed30e0f54d43d41c67c661d158b3d0830c3ea2"],"m/1/0/2":["02972eae7e4302e319c266578e14a07839c1e788296a92906e6d66d938211dad5f","039ed6b488f1571ad6527acd6b6c5b8453eacf6665dc5cb7852e33d1c8ea73f9fe"],"m/1/0/3":["02bec4728888c2c045108353994bae5731ec7a7b41459023b0023e10b8d616bd30","03ce1efe16214c9eac595382e46a68143dd11a335b3f7c971ddd719ac544a5fc4b"],"m/1/0/4":["030e2df1d341568225d8dfbe5d07e98dae9f90e0f43e19dcc68c998a6ed7bcc1f0","0380f4c07dc84faf42d51779f104aa6e3b5c3ce2d7684b3cb76d49faeefc2b69d6"],"m/1/0/5":["029a54ddaa25f433b493f4b72df8c1d41be2c4d2963b8b61ee63cc86d16c12d066","021567c95e0317442e7367aa4e3378dd46c5bcef5860f789272fea83b917de0669"],"m/1/0/6":["03590320d80b61cc0874b579f467c9b5ccc50d9ef875bcf6bdd12e2d0c211e8973","03ee4677b6ee89a9d355851f2230506c6897ff219062c0df4ad9a85c60f3535f93"],"m/1/0/7":["03caf98ab1c9b79d1dc8029453a6137c08787b04043b79af3cb42d41d2d3f1338f","023f39ae4e2f4f3887d5fc58e0d3a0d7ee267dc04aa257c75b6b2d67d2f5580f81"],"m/1/0/8":["0352a2a3ea8209c9a2b633d788796ac2d16c08022440e04a77ab2835c7f971d266","0291bc248b3da997f35e8fae98a75a91fdac2819d74c4e270899338d48f7389e87"],"m/1/0/9":["02468d32d9c3c62418d506d4cd0da6cd2022d5bcafdb5f847cf7bde7a48ec6848b","032713d90d12eb6a072f3c1db6c0d3b680d3f78883016135fc0f78e8193d41d4b4"],"m/1/0/10":["034863cc6bab9b059be53413ba75c5fc286647c20d7f9e5512ef4754ea301dd1ce","03a33ab9c32a2264ee2464ebbb5892f0e34acf0fdede4f87395a89e9dacdd4930e"],"m/1/0/11":["031e19296695bfe8a96ba3bf58afa805ee1bd5471fddb3929b1678d69d442d69c9","0270feb33956fd9e937019d629523e26437493c0856514011e6aec88baf7721295"],"m/1/0/12":["03cce695d3c3843bf73e851b2446a77d7e235e5b80b4f4474f9946292eb8218742","039ea96c8822f0ec7ed28308d277f3e730480d7573579cd11b89aef4364cd9ffeb"],"m/1/0/13":["02ab4ac38eb405e822d12c0f0f354f04f9ee1d991dde887a5c1171096fe503158f","036809e60cae1203da8884ea1f85d4669ce6e053f8ba605d775e271b70ab4f6787"],"m/1/0/14":["039d61da23a8610fa0ee58eb37d7cea7ea9396c79153da97280ccf5e46718e3bac","03015c27bcc778682781fd6ad30aa6041db0b7e24270818cdceece0043ccc34b26"],"m/1/0/15":["03c088ed669132835d2728b0ecf294271c8388988c6ae264d43ca24f50e4005f81","03e2c118c9445a2ddc4c8afeb0ba49e21be3f818a483d346418b8922b8a371a2b7"],"m/1/0/16":["02bba7df9847f463c6b23eca37a4bd6efa3801a52b8ddfad804d902e783b70c81c","03764b657f23996e31c64a701facc1cbeb0c9edfdd605e2c1ed36cf48197565d45"],"m/1/0/17":["020445179c522295b89bf4bfd582eb03422e3fa20dcd29263925e9f44282d476d8","036e47bdd32f3061aed1c1f8c2a32b038c7b72391cb1f80ebfc150e58f88372766"],"m/1/0/18":["024d88c4bfcbba713d49e1edcd035234aaa1ee76ad7bcf75bf074a16658a6b0b6d","02b861e7a20d89f6875d2e44c78dbadb99503e282e5e60e9f65657af6fea81d425"],"m/1/0/19":["023a8ca9d5300181f157e1930d3b0800eebe7683d8df72e6cbf28834dbf1be5d60","026053c4f84c10d15890c0b254522972931bc2d5b7cdf9c1f9f3137c22edf3ecd3"],"m/1/0/20":["03137c66e9f3d61aba659f408d77a293fa0f3fea4ccb911074a681d6f61a55d023","0291aa1bbfbef59b16b0e37e185a706c589d448cb02e860c5df9c9d7242ecc739f"],"m/1/0/21":["03c08673e0cae55318bc9dcc4b5f11eb3ff71d42de04015e255dde3fd8cba7e09e","02423d4eab06cd5b26e71d145283523c011d58032700c517f00b328d2c90cf109f"]}},"txProposals":{"txps":[{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543144016,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543144645},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543144016},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543170040},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/0"],"comment":"blablabla","builderObj":{"valueInSat":"29000000","valueOutSat":"8900000","feeSat":"10000","remainderSat":"20090000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":0,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a9010000009300493046022100ccbb8f398f74a76236629b8499ffc6f9518a2091f5a61a9a352c0a10f615961e022100b8f0769c76cf33bec3d7f81d9da2b74cf6e8a5e0a24ee5f48172854d8bcdbfa101475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff02a0cd8700000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac908c32010000000017a914560c292066792531164149c5ed63ad2793a61b928700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543188745,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543189341},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543188745,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543206819},"rejectedBy":{},"sentTs":1405543207304,"sentTxid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","inputChainPaths":["m/45\'/0/0/0"],"comment":"que parece","builderObj":{"valueInSat":"29000000","valueOutSat":"9000000","feeSat":"10000","remainderSat":"19990000","hashToScriptMap":{"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj":"5221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852ae"},"selectedUtxos":[{"address":"2NBtv6DdXj8HBunyGqpW9H8bUtW5x3rfVTj","txid":"a9f4dda3f092e37244bc4e77ea921fed01d5b8ea49613dfdc0dc8afdd70190b5","vout":1,"ts":1405543855,"scriptPubKey":"a914cc93216398b77b5f8c451ca3a357bef961678be987","amount":0.29,"confirmations":1,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001b59001d7fd8adcc0fd3d6149eab8d501ed1f92ea774ebc4472e392f0a3ddf4a901000000da00483045022035423cc74824ba904907678dda3b62a20a787b96d1b3e9f3e9546f9c57f4e45902210080a1ff1c39f458ac1642b9e948bd62fd70563b5252e749cc8fc642cd763ee830014730440220524a13f36cfb03caa246d7d84de634ec9386f2c39c19bfa926037f48da86262b022050e58a6503d105ad2805f86806810a1aa7f20d6271e1340b42fa91ab6a30f3e801475221028a4b63f26253f3a8731577b8e1ee480950ad5833ebbf106fe3463bfc07cc3b90210332efa054c08cb77506a35ee0762cb7156f244566703ec08e433568ec0397bec852aeffffffff0240548900000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf00531010000000017a9146130a9d51f996b7a1b9d3e10c80930834251909d8700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543505848,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543505848,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543590221},"rejectedBy":{},"sentTs":1405543610315,"sentTxid":"6fe851b54b777a75fe80fa204dc674395e2af69efb1f7c0017e909eb82c3d914","inputChainPaths":["m/45\'/0/1/1"],"comment":"mandaaaaaaa","builderObj":{"valueInSat":"19990000","valueOutSat":"19980000","feeSat":"10000","remainderSat":"0","hashToScriptMap":{"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts":"5221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52ae"},"selectedUtxos":[{"address":"2N277q5r8Ab6XLJNCjXXFdh5itDJRQCv9ts","txid":"169bc92693dd2e27724eeba81e54210e842035bd3af6c52e6a6a5e908f1a4f66","vout":1,"ts":1405543157,"scriptPubKey":"a9146130a9d51f996b7a1b9d3e10c80930834251909d87","amount":0.1999,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"0100000001664f1a8f905e6a6a2ec5f63abd3520840e21541ea8eb4e72272edd9326c99b1601000000db0048304502206b18b3dba2646c552469d8ef52d7656f6a65f563032530f622abdfd8bd4c5cee022100e804b406eddebbc827646141e74dc64c76a770ed4e35183ffd35d265ad9f7d3b01483045022100f6c013638ff0a316b1baa93dfffba6a98cf3033c133e8bd899e933c9c3e47ce10220530f40e7ea52ae58bec695edbec6d566d2ee8e7b5f33f95e33093ad1e29a125401475221020389327ee8ae7d0ee3f8187842d23a4070bdd8a27c0bcddd05d80ef39009253d21025c9b49bdf17d97bd82ea1b87793082f857247f0f9b999937a166ec994bb1b41f52aeffffffff01e0de3001000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac00000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543781381,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543782017},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543781381},"rejectedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543794590},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"1","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c000000009200483045022064d877bc5171fbaef909c2a1a924e0023b3ccc0b530cb46653f06ecb230283e8022100bc6658d60ad4f7120d9226c8f6eada87f3b0388f73c458011988bab36e78ba15014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a91421c4a435d9ac263ec55b35a1a5ca95e979639b9b8700000000"}},{"creator":"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5","createdTs":1405543835343,"seenBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343,"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543835968},"signedBy":{"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543835343},"rejectedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543850998},"sentTs":null,"sentTxid":null,"inputChainPaths":["m/45\'/0/0/1"],"comment":"2","builderObj":{"valueInSat":"29000000","valueOutSat":"1000000","feeSat":"10000","remainderSat":"27990000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":0,"signaturesAdded":1,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c0000000092004830450220302baae7de2e0f102bf3af2d5f450f673e51bd143020141a769ccdcdf16af188022100e7abc087c76050ed649e7139a5a136969e74e24a8d8f6223d3219ad033a26451014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff0240420f00000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288acf017ab010000000017a9148b102abba0729fb0690c61cf7187064d692d43d78700000000"}},{"creator":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","createdTs":1405543869803,"seenBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543870411},"signedBy":{"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba":1405543869803,"02b0c868a3889cd0cfc0e7fef9eaa6d85d7cf6f7573ae5c9d1d13645d22e2eb7e5":1405543890406},"rejectedBy":{},"sentTs":1405543890913,"sentTxid":"6a0f61574ad65e537e7e99298968db565f97b894b61f4c8f8fac8fcaedb83e2b","inputChainPaths":["m/45\'/0/0/1"],"comment":"3","builderObj":{"valueInSat":"29000000","valueOutSat":"1100000","feeSat":"10000","remainderSat":"27890000","hashToScriptMap":{"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb":"52210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352ae"},"selectedUtxos":[{"address":"2N4eyXKikdnnUT4S74MRNAYqXChhUYmZ1Sb","txid":"6c9da5b0da4bab0d576033325e987b10ccf2b9bf479d306b6aae36efeaa56892","vout":0,"ts":1405543698,"scriptPubKey":"a9147d274ac50968d7823b6cbc1b38770deb7157995387","amount":0.29,"confirmationsFromCache":false}],"inputsSigned":1,"signaturesAdded":2,"signhash":1,"spendUnconfirmed":true,"tx":"01000000019268a5eaef36ae6a6b309d47bfb9f2cc107b985e323360570dab4bdab0a59d6c00000000db00483045022100a8ce7907f9fd7dd41dd65c2dec425e008efea06ee7c80787c10c0e210fbf181302207712c0fdd1cb25836ac1fc2fd303c1e26b85e8980417719b9ed50e977a9693ec01483045022100d1780c4f028cd898920aca3eaceba352ed9306cd17f019ae2f634e8facad149a02203c84ab2093da8e22577e93f27a732f0728d4e6db0c749f3cd3d898d6a025152a014752210359c6d0d0d31f83301169901a6ffad9535f14014b5ab3b43561dbb2436a7b813821037d06f713f13a11967fd5edca265ff4c77528693a712c482256505693e4890d9352aeffffffff02e0c81000000000001976a91485eb47fe98f349065d6f044e27a4ac541af79ee288ac5091a9010000000017a914cc1cab78458b1a951b91c6dcd7eeeeb682f506388700000000"}}],"walletId":"55d4bd062d32f90a","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPdWUAmaaopPftevC72Jtiu19V8ee5XijL9JvogqfR95uVrL85f8yBdQMq3KyQtG3Q91yWQb3XDbWWpcdWFDAmJ7Xy2XWkGJu","networkName":"testnet","privateKeyCache":{"m/45\'/0/0/0":"b6fd8d1a079efd523da34f31ba81f544fc3d0a728a8a98299d8980682518e79c","m/45\'/0/1/1":"0f4d52d2a99e4c8c1c2edf09fef12407c3abd2304b961198c3f131a8c8443a13","m/45\'/0/0/1":"de5c191c343bd6017b98708c03344849624a14e2c167cfd6eb8dcb075d139293"}},"addressBook":{"msj42CCGruhRsFrGATiUuh25dtxYtnpbTx":{"hidden":false,"createdTs":1405543109222,"copayerId":"02c7b87033e4357d8afc6ab7fe31fff054772ea6251f0d9c8a835b1c1ac74f6fba","label":"faucet","signature":"3045022067576e5b37f2707a8dc66e57511ad9b10a3125bd95193fff6f8f6402969c3bf3022100adff9f417db07d88face13b3d13f422740d4421440cade1a205684dfdc5d733a"}}}'; - - - -var legacy1 = '{"opts":{"id":"48ba2f1ffdfe9708","spendUnconfirmed":true,"requiredCopayers":1,"totalCopayers":1,"name":"pepe wallet","version":"0.4.7"},"networkNonce":"5405f06b00000001","networkNonces":[],"publicKeyRing":{"walletId":"48ba2f1ffdfe9708","networkName":"testnet","requiredCopayers":1,"totalCopayers":1,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1},{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}],"copayersBackup":["0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f"],"copayersExtPubKeys":["tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"48ba2f1ffdfe9708","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm","networkName":"testnet"},"addressBook":{}}'; From bdaf40de484fcac9e8bc201c5b8389ee207d3aab Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 10 Oct 2014 17:58:19 -0300 Subject: [PATCH 044/182] add redirs to internal pages --- js/controllers/addresses.js | 3 + js/controllers/join.js | 5 +- js/controllers/more.js | 2 + js/controllers/send.js | 2 + js/controllers/sidebar.js | 12 +-- js/controllers/transactions.js | 2 + js/models/Wallet.js | 106 +++++++++++++-------------- js/services/controllerUtils.js | 16 +++- test/{test.Profile.js => Profile.js} | 2 +- test/mocks/FakeBlockchain.js | 2 + views/copayers.html | 1 - 11 files changed, 88 insertions(+), 65 deletions(-) rename test/{test.Profile.js => Profile.js} (98%) diff --git a/js/controllers/addresses.js b/js/controllers/addresses.js index 3432ef8c5..522516c1d 100644 --- a/js/controllers/addresses.js +++ b/js/controllers/addresses.js @@ -2,6 +2,9 @@ angular.module('copayApp.controllers').controller('AddressesController', function($scope, $rootScope, $timeout, $modal, controllerUtils) { + controllerUtils.redirIfNotComplete(); + + $scope.loading = false; $scope.showAll = false; var w = $rootScope.wallet; diff --git a/js/controllers/join.js b/js/controllers/join.js index 01e658b23..9260332eb 100644 --- a/js/controllers/join.js +++ b/js/controllers/join.js @@ -139,9 +139,8 @@ angular.module('copayApp.controllers').controller('JoinController', notification.error('Unknown error'); controllerUtils.onErrorDigest(); } else { - $scope.loading = false; - $rootScope.wallet = w; - controllerUtils.bindWallet(w, $scope); + controllerUtils.installWalletHandlers($scope, w); + controllerUtils.setFocusedWallet(w); } }); } diff --git a/js/controllers/more.js b/js/controllers/more.js index dd7c6b1c7..b867b2aae 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -2,9 +2,11 @@ angular.module('copayApp.controllers').controller('MoreController', function($scope, $rootScope, $location, $filter, backupService, controllerUtils, notification, rateService) { + controllerUtils.redirIfNotComplete(); var w = $rootScope.wallet; $scope.isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; + $scope.unitOpts = [{ name: 'Satoshis (100,000,000 satoshis = 1BTC)', shortName: 'SAT', diff --git a/js/controllers/send.js b/js/controllers/send.js index ad6ab7093..4025f444d 100644 --- a/js/controllers/send.js +++ b/js/controllers/send.js @@ -4,6 +4,8 @@ var preconditions = require('preconditions').singleton(); angular.module('copayApp.controllers').controller('SendController', function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) { + controllerUtils.redirIfNotComplete(); + var w = $rootScope.wallet; preconditions.checkState(w); preconditions.checkState(w.settings.unitToSatoshi); diff --git a/js/controllers/sidebar.js b/js/controllers/sidebar.js index 20520aa9a..971de1820 100644 --- a/js/controllers/sidebar.js +++ b/js/controllers/sidebar.js @@ -36,11 +36,13 @@ angular.module('copayApp.controllers').controller('SidebarController', function( $scope.refresh = function() { var w = $rootScope.wallet; - w.sendWalletReady(); - if ($rootScope.addrInfos.length > 0) { - controllerUtils.updateBalance(function() { - $rootScope.$digest(); - }); + if (w.isReady()) { + w.sendWalletReady(); + if ($rootScope.addrInfos.length > 0) { + controllerUtils.updateBalance(function() { + $rootScope.$digest(); + }); + } } }; diff --git a/js/controllers/transactions.js b/js/controllers/transactions.js index 914a07137..d64583a8f 100644 --- a/js/controllers/transactions.js +++ b/js/controllers/transactions.js @@ -3,6 +3,8 @@ var bitcore = require('bitcore'); angular.module('copayApp.controllers').controller('TransactionsController', function($scope, $rootScope, $timeout, controllerUtils, notification, rateService) { + controllerUtils.redirIfNotComplete(); + var w = $rootScope.wallet; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 8b206ccea..4344e6a36 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -64,7 +64,7 @@ function Wallet(opts) { opts.reconnectDelay = opts.reconnectDelay || 500; var networkName = Wallet.obtainNetworkName(opts); - preconditions.checkState( (opts.network && opts.blockchain) || networkName); + preconditions.checkState((opts.network && opts.blockchain) || networkName); opts.network = opts.network || Wallet._newAsync(opts.networkOpts[networkName]); opts.blockchain = opts.blockchain || Wallet._newInsight(opts.blockchainOpts[networkName]);; @@ -283,7 +283,7 @@ Wallet.read = function(walletId, readOpts, cb) { Wallet.obtainNetworkName = function(obj) { return obj.networkName || (obj.opts ? obj.opts.networkName : null) || - (obj.publicKeyRing ? (obj.publicKeyRing.networkName || obj.publicKeyRing.network.name) : null) || + (obj.publicKeyRing ? (obj.publicKeyRing.networkName || obj.publicKeyRing.network.name) : null) || (obj.privateKey ? obj.privateKey.networkName : null); }; @@ -297,6 +297,14 @@ Wallet.prototype.seedCopayer = function(pubKey) { this.seededCopayerId = pubKey; }; + +Wallet.prototype._newAddresses = function(dontUpdateUx) { + if (this.publicKeyRing.isComplete()) { + this.subscribeToAddresses(); + }; + this.emit('newAddresses', dontUpdateUx); +}; + /** * @desc Handles an 'indexes' message. * @@ -307,14 +315,13 @@ Wallet.prototype.seedCopayer = function(pubKey) { * * @param {string} senderId - the sender id * @param {Object} data - the data recived, {@see HDParams#fromList} - * @emits publicKeyRingUpdated */ Wallet.prototype._onIndexes = function(senderId, data) { log.debug('Wallet:' + this.id + ' RECV INDEXES:', data); var inIndexes = HDParams.fromList(data.indexes); var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes); if (hasChanged) { - this.emit('publicKeyRingUpdated'); + this._newAddresses(); this.store(); } }; @@ -352,11 +359,10 @@ Wallet.prototype.changeSettings = function(settings) { * @param {Object} data - the data recived, {@see HDParams#fromList} * @param {Object} data.publicKeyRing - data to be deserialized into a {@link PublicKeyRing} * using {@link PublicKeyRing#fromObj} - * @emits publicKeyRingUpdated * @emits connectionError */ Wallet.prototype._onPublicKeyRing = function(senderId, data) { - log.debug('Wallet:' + this.id +' RECV PUBLICKEYRING:', data); + log.debug('Wallet:' + this.id + ' RECV PUBLICKEYRING:', data); var inPKR = PublicKeyRing.fromObj(data.publicKeyRing); var wasIncomplete = !this.publicKeyRing.isComplete(); @@ -365,7 +371,7 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) { try { hasChanged = this.publicKeyRing.merge(inPKR, true); } catch (e) { - log.debug('Wallet:' + this.id +'## WALLET ERROR', e); + log.debug('Wallet:' + this.id + '## WALLET ERROR', e); this.emit('connectionError', e.message); return; } @@ -377,7 +383,7 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) { if (this.publicKeyRing.isComplete()) { this._lockIncomming(); } - this.emit('publicKeyRingUpdated'); + this._newAddresses(); this.store(); } }; @@ -494,7 +500,7 @@ Wallet.prototype._checkSentTx = function(ntxid, cb) { */ Wallet.prototype._onTxProposal = function(senderId, data) { var self = this; - log.debug('Wallet:' + this.id +' RECV TXPROPOSAL: ', data); + log.debug('Wallet:' + this.id + ' RECV TXPROPOSAL: ', data); var m; try { @@ -547,7 +553,7 @@ Wallet.prototype._onTxProposal = function(senderId, data) { */ Wallet.prototype._onReject = function(senderId, data) { preconditions.checkState(data.ntxid); - log.debug('Wallet:' + this.id +' RECV REJECT:', data); + log.debug('Wallet:' + this.id + ' RECV REJECT:', data); var txp = this.txProposals.get(data.ntxid); @@ -580,7 +586,7 @@ Wallet.prototype._onReject = function(senderId, data) { */ Wallet.prototype._onSeen = function(senderId, data) { preconditions.checkState(data.ntxid); - log.debug('Wallet:' + this.id +' RECV SEEN:', data); + log.debug('Wallet:' + this.id + ' RECV SEEN:', data); var txp = this.txProposals.get(data.ntxid); txp.setSeen(senderId); @@ -608,7 +614,7 @@ Wallet.prototype._onSeen = function(senderId, data) { */ Wallet.prototype._onAddressBook = function(senderId, data) { preconditions.checkState(data.addressBook); - log.debug('Wallet:' + this.id +' RECV ADDRESSBOOK:', data); + log.debug('Wallet:' + this.id + ' RECV ADDRESSBOOK:', data); var rcv = data.addressBook; var hasChange; for (var key in rcv) { @@ -642,7 +648,7 @@ Wallet.prototype.updateTimestamp = function(ts) { * Triggers a call to {@link Wallet#sendWalletReady} */ Wallet.prototype._onNoMessages = function() { - log.debug('Wallet:' + this.id +' No messages at the server. Requesting peer sync from: ' + (this.lastTimestamp + 1)); + log.debug('Wallet:' + this.id + ' No messages at the server. Requesting peer sync from: ' + (this.lastTimestamp + 1)); this.sendWalletReady(null, parseInt((this.lastTimestamp + 1) / 1000)); this.updateTimestamp(parseInt(Date.now() / 1000)); }; @@ -662,18 +668,15 @@ Wallet.prototype._onData = function(senderId, data, ts) { preconditions.checkArgument(data.type); preconditions.checkArgument(ts); preconditions.checkArgument(_.isNumber(ts)); - log.debug('Wallet:' + this.id +' RECV', senderId, data); + log.debug('Wallet:' + this.id + ' RECV', senderId, data); -console.log('[Wallet.js.635]'); //TODO this.updateTimestamp(ts); -console.log('[Wallet.js.638]'); //TODO if (data.type !== 'walletId' && this.id !== data.walletId) { - log.debug('Wallet:' + this.id +' Received corrupt message:', data) + log.debug('Wallet:' + this.id + ' Received corrupt message:', data) this.emit('corrupt', senderId); return; } -console.log('[Wallet.js.644]'); //TODO switch (data.type) { // This handler is repeaded on WalletFactory (#join). TODO @@ -682,11 +685,11 @@ console.log('[Wallet.js.644]'); //TODO break; case 'walletReady': -console.log('[Wallet.js.653]', this.lastMessageFrom[senderId] ); //TODO + console.log('[Wallet.js.653]', this.lastMessageFrom[senderId]); //TODO if (this.lastMessageFrom[senderId] !== 'walletReady') { -console.log('[Wallet.js.656]'); //TODO - log.debug('Wallet:' + this.id +' peer Sync received. since: ' + (data.sinceTs || 0)); + console.log('[Wallet.js.656]'); //TODO + log.debug('Wallet:' + this.id + ' peer Sync received. since: ' + (data.sinceTs || 0)); this.sendPublicKeyRing(senderId); this.sendAddressBook(senderId); this.sendAllTxProposals(senderId, data.sinceTs); // send old txps @@ -728,7 +731,7 @@ console.log('[Wallet.js.656]'); //TODO */ Wallet.prototype._onConnect = function(newCopayerId) { if (newCopayerId) { - log.debug('Wallet:' + this.id +'#### Setting new COPAYER:', newCopayerId); + log.debug('Wallet:' + this.id + '#### Setting new COPAYER:', newCopayerId); this.sendWalletId(newCopayerId); } @@ -745,7 +748,7 @@ Wallet.prototype.getNetworkName = function() { }; /** - * @desc + * @desc * @return {bool} */ Wallet.prototype.isTestnet = function() { @@ -869,7 +872,7 @@ Wallet.prototype._setBlockchainListeners = function() { self.blockchain.removeAllListeners(); self.blockchain.on('reconnect', function(attempts) { - log.debug('Wallet:' + self.id +'blockchain reconnect event'); + log.debug('Wallet:' + self.id + 'blockchain reconnect event'); self.emit('insightReconnected'); // Subscription should persist? TODO @@ -877,11 +880,11 @@ Wallet.prototype._setBlockchainListeners = function() { }); self.blockchain.on('disconnect', function() { - log.debug('Wallet:' + self.id +'blockchain disconnect event'); + log.debug('Wallet:' + self.id + 'blockchain disconnect event'); self.emit('insightError'); }); self.blockchain.on('tx', function(tx) { - log.debug('Wallet:' + self.id +'blockchain tx event'); + log.debug('Wallet:' + self.id + 'blockchain tx event'); var addresses = self.getAddressesInfo(); var addr = _.findWhere(addresses, { addressStr: tx.address @@ -903,7 +906,6 @@ Wallet.prototype._setBlockchainListeners = function() { * @emits data * * @emits ready - * @emits publicKeyRingUpdated * @emits txProposalsUpdated * * @TODO: FIX PROTOCOL -- emit with a space is shitty @@ -914,7 +916,7 @@ Wallet.prototype.netStart = function() { var net = this.network; if (net.started) { - log.debug('Wallet:' + self.id +' Wallet networking was ready') + log.debug('Wallet:' + self.id + ' Wallet networking was ready') self.emit('ready', net.getPeer()); return; } @@ -947,13 +949,12 @@ Wallet.prototype.netStart = function() { net.start(startOpts, function() { log.debug('Wallet:' + self.id + ' Networking ready:', net.copayerId); self._setBlockchainListeners(); - self.subscribeToAddresses(); self.emit('ready', net.getPeer()); setTimeout(function() { - self.emit('publicKeyRingUpdated', true); + self._newAddresses(true); // no connection logic for now self.emit('txProposalsUpdated'); - }, 10); + }, 0); }); }; @@ -1024,7 +1025,7 @@ Wallet.prototype.store = function(cb) { var val = this.toObj(); var key = 'wallet::' + this.id + ((val.opts && val.opts.name) ? '_' + val.opts.name : ''); this.storage.set(key, val, function(err) { - log.debug('Wallet:' + self.id +' stored'); + log.debug('Wallet:' + self.id + ' stored'); if (cb) cb(err); }); @@ -1186,7 +1187,7 @@ Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) { */ Wallet.prototype.sendTxProposal = function(ntxid, recipients) { preconditions.checkArgument(ntxid); - log.debug('Wallet:' + this.id +' ### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals); + log.debug('Wallet:' + this.id + ' ### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals); this.send(recipients, { type: 'txProposal', txProposal: this.txProposals.get(ntxid).toObjTrim(), @@ -1200,7 +1201,7 @@ Wallet.prototype.sendTxProposal = function(ntxid, recipients) { */ Wallet.prototype.sendSeen = function(ntxid) { preconditions.checkArgument(ntxid); - log.debug('Wallet:' + this.id +' ### SENDING seen: ' + ntxid + ' TO: All'); + log.debug('Wallet:' + this.id + ' ### SENDING seen: ' + ntxid + ' TO: All'); this.send(null, { type: 'seen', ntxid: ntxid, @@ -1214,7 +1215,7 @@ Wallet.prototype.sendSeen = function(ntxid) { */ Wallet.prototype.sendReject = function(ntxid) { preconditions.checkArgument(ntxid); - log.debug('Wallet:' + this.id +' ### SENDING reject: ' + ntxid + ' TO: All'); + log.debug('Wallet:' + this.id + ' ### SENDING reject: ' + ntxid + ' TO: All'); this.send(null, { type: 'reject', ntxid: ntxid, @@ -1227,7 +1228,7 @@ Wallet.prototype.sendReject = function(ntxid) { * @param {string[]} [recipients] - the pubkeys of the recipients */ Wallet.prototype.sendWalletReady = function(recipients, sinceTs) { - log.debug('Wallet:' + this.id +' ### SENDING WalletReady TO:', recipients || 'All'); + log.debug('Wallet:' + this.id + ' ### SENDING WalletReady TO:', recipients || 'All'); this.send(recipients, { type: 'walletReady', @@ -1242,7 +1243,7 @@ Wallet.prototype.sendWalletReady = function(recipients, sinceTs) { * @param {string[]} [recipients] - the pubkeys of the recipients */ Wallet.prototype.sendWalletId = function(recipients) { - log.debug('Wallet:' + this.id +' ### SENDING walletId TO:', recipients || 'All', this.id); + log.debug('Wallet:' + this.id + ' ### SENDING walletId TO:', recipients || 'All', this.id); this.send(recipients, { type: 'walletId', @@ -1257,7 +1258,7 @@ Wallet.prototype.sendWalletId = function(recipients) { * @param {string[]} [recipients] - the pubkeys of the recipients */ Wallet.prototype.sendPublicKeyRing = function(recipients) { - log.debug('Wallet:' + this.id +' ### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj()); + log.debug('Wallet:' + this.id + ' ### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj()); var publicKeyRing = this.publicKeyRing.toObj(); this.send(recipients, { @@ -1273,7 +1274,7 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) { */ Wallet.prototype.sendIndexes = function(recipients) { var indexes = HDParams.serialize(this.publicKeyRing.indexes); - log.debug('Wallet:' + this.id +' ### INDEXES TO:', recipients || 'All', indexes); + log.debug('Wallet:' + this.id + ' ### INDEXES TO:', recipients || 'All', indexes); this.send(recipients, { type: 'indexes', @@ -1287,7 +1288,7 @@ Wallet.prototype.sendIndexes = function(recipients) { * @param {string[]} recipients - the pubkeys of the recipients */ Wallet.prototype.sendAddressBook = function(recipients) { - log.debug('Wallet:' + this.id +' ### SENDING addressBook TO:', recipients || 'All', this.addressBook); + log.debug('Wallet:' + this.id + ' ### SENDING addressBook TO:', recipients || 'All', this.addressBook); this.send(recipients, { type: 'addressbook', addressBook: this.addressBook, @@ -1448,23 +1449,23 @@ Wallet.prototype.sendTx = function(ntxid, cb) { var tx = txp.builder.build(); if (!tx.isComplete()) throw new Error('Tx is not complete. Can not broadcast'); - log.debug('Wallet:' + this.id +' Broadcasting Transaction'); + log.debug('Wallet:' + this.id + ' Broadcasting Transaction'); var scriptSig = tx.ins[0].getScript(); var size = scriptSig.serialize().length; var txHex = tx.serialize().toString('hex'); - log.debug('Wallet:' + this.id +' Raw transaction: ', txHex); + log.debug('Wallet:' + this.id + ' Raw transaction: ', txHex); var self = this; this.blockchain.broadcast(txHex, function(err, txid) { - log.debug('Wallet:' + self.id +' BITCOIND txid:', txid); + log.debug('Wallet:' + self.id + ' BITCOIND txid:', txid); if (txid) { self.txProposals.get(ntxid).setSent(txid); self.sendTxProposal(ntxid); self.store(); return cb(txid); } else { - log.debug('Wallet:' + self.id +' Sent failed. Checking if the TX was sent already'); + log.debug('Wallet:' + self.id + ' Sent failed. Checking if the TX was sent already'); self._checkSentTx(ntxid, function(txid) { if (txid) self.store(); @@ -2429,11 +2430,11 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos try { b = new Builder(opts) - .setUnspent(utxos) - .setOutputs([{ - address: toAddress, - amountSatStr: amountSatStr, - }]); + .setUnspent(utxos) + .setOutputs([{ + address: toAddress, + amountSatStr: amountSatStr, + }]); } catch (e) { log.debug(e.message); return; @@ -2479,11 +2480,10 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos * * Triggers a wallet {@link Wallet#store} call * @param {Function} callback - called when all indexes have been updated. Receives an error, if any, as first argument - * @emits publicKeyRingUpdated */ Wallet.prototype.updateIndexes = function(callback) { var self = this; - log.debug('Wallet:' + this.id +' Updating indexes...'); + log.debug('Wallet:' + this.id + ' Updating indexes...'); var tasks = this.publicKeyRing.indexes.map(function(index) { return function(callback) { @@ -2493,8 +2493,8 @@ Wallet.prototype.updateIndexes = function(callback) { async.parallel(tasks, function(err) { if (err) callback(err); - log.debug('Wallet:' + self.id +' Indexes updated'); - self.emit('publicKeyRingUpdated'); + log.debug('Wallet:' + self.id + ' Indexes updated'); + self._newAddresses(); self.store(); callback(); }); diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index 3ece396a5..eee1e9efa 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -5,6 +5,19 @@ angular.module('copayApp.services') .factory('controllerUtils', function($rootScope, $sce, $location, $filter, notification, $timeout, uriHandler, rateService) { var root = {}; + + root.redirIfNotComplete = function() { + var w = $rootScope.wallet; + if (w) { + if (!w.isReady()) { + $location.path('/copayers'); + } + } else { + $location.path('/'); + } + }; + + root.redirIfLogged = function() { var w = $rootScope.wallet; if (w) { @@ -80,7 +93,7 @@ angular.module('copayApp.services') } }); - w.on('publicKeyRingUpdated', function(dontDigest) { + w.on('newAddresses', function(dontDigest) { if (root.isFocusedWallet(wid)) { root.updateAddressList(); if (!dontDigest) { @@ -205,7 +218,6 @@ angular.module('copayApp.services') $rootScope.wallet = w; root.updateAddressList(); - root.redirIfLogged(); }; diff --git a/test/test.Profile.js b/test/Profile.js similarity index 98% rename from test/test.Profile.js rename to test/Profile.js index 3e2c87639..4ac4598c1 100644 --- a/test/test.Profile.js +++ b/test/Profile.js @@ -126,7 +126,7 @@ describe('Profile model', function() { p.addWallet('123', {}, function(err) { p.addWallet('234', {}, function(err) { p.addWallet('345', {}, function(err) { - _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '234', '345']); + _.pluck(p.listWallets(), 'id').sort().should.deep.equal(['123', '234', '345']); p.deleteWallet('234', function(err) { _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '345']); done(); diff --git a/test/mocks/FakeBlockchain.js b/test/mocks/FakeBlockchain.js index 7de66350d..b77a5a931 100644 --- a/test/mocks/FakeBlockchain.js +++ b/test/mocks/FakeBlockchain.js @@ -14,6 +14,8 @@ FakeBlockchain.prototype.getTransactions = function(addresses, cb) { cb(null, []); }; +FakeBlockchain.prototype.subscribe = function() { +}; FakeBlockchain.prototype.fixUnspent = function(u) { this.u = u; diff --git a/views/copayers.html b/views/copayers.html index 82553d0ec..742e9046e 100644 --- a/views/copayers.html +++ b/views/copayers.html @@ -35,7 +35,6 @@
    Waiting Copayer From b03a3f139110fa64d935bafa64eb654862a07eb6 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 11 Oct 2014 08:43:51 -0300 Subject: [PATCH 045/182] check if there is not profile, to redir --- config.js | 2 +- js/controllers/home.js | 11 +++++++- js/models/Identity.js | 47 +++++++++++++++++++++-------------- js/models/Profile.js | 7 ++++++ js/models/Storage.js | 8 +++--- test/{models => }/Identity.js | 0 test/Profile.js | 2 +- views/createProfile.html | 1 - views/home.html | 2 -- 9 files changed, 52 insertions(+), 28 deletions(-) rename test/{models => }/Identity.js (100%) diff --git a/config.js b/config.js index 3ac6bde6e..805ff00d6 100644 --- a/config.js +++ b/config.js @@ -41,7 +41,7 @@ var defaultConfig = { }, // local encryption/security config - passphrase: { + passphraseConfig: { iterations: 100, storageSalt: 'mjuBtGybi/4=', }, diff --git a/js/controllers/home.js b/js/controllers/home.js index 94ebc6d35..3a3797027 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -1,9 +1,18 @@ 'use strict'; angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) { - controllerUtils.redirIfLogged(); + $scope.retreiving =true; + copay.Identity.anyProfile({ + pluginManager: pluginManager, + }, function(any) { + $scope.retreiving =false; + if (!any) + $location.path('/createProfile'); + }); + + $scope.openProfile = function(form) { if (form && form.$invalid) { notification.error('Error', 'Please enter the required fields'); diff --git a/js/models/Identity.js b/js/models/Identity.js index c540f2c97..fc9987cb6 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -26,26 +26,8 @@ var Storage = module.exports.Storage = require('./Storage'); function Identity(email, password, opts) { preconditions.checkArgument(opts); - var storageOpts = {}; - - if (opts.pluginManager) { - storageOpts = _.clone({ - db: opts.pluginManager.get('DB'), - passphrase: opts.passphrase, - }); - /* - * TODO (plugins for other services) - * - * blockchainOpts = { - * provider: Insight... - * } - */ - } - storageOpts.password = password; - - this.storage = Identity._newStorage(storageOpts); - this.storage.setPassword(password); + this.storage = Identity._getStorage(opts, password); this.networkOpts = { 'livenet': opts.network.livenet, 'testnet': opts.network.testnet, @@ -102,6 +84,33 @@ Identity._newAsync = function(opts) { +Identity._getStorage = function(opts, password) { + var storageOpts = {}; + + if (opts.pluginManager) { + storageOpts = _.clone({ + db: opts.pluginManager.get('DB'), + passphraseConfig: opts.passphraseConfig, + }); + } + if (password) + storageOpts.password = password; + + return Identity._newStorage(storageOpts); +}; + +/** + * check if any profile exists on storage + * + * @param opts.storageOpts + * @param cb + */ + + +Identity.anyProfile = function(opts, cb) { + var storage = Identity._getStorage(opts); + Profile.any(storage,cb); +}; /** * creates and Identity diff --git a/js/models/Profile.js b/js/models/Profile.js index 37d6fe2ca..8f74a3373 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -43,6 +43,13 @@ Profile.create = function(email, password, storage, cb) { }); }; + +Profile.any = function(storage, cb) { + storage.getFirst(Profile.key(''), function(err, val) { + return cb(val ? true : false); + }); +}; + Profile.open = function(email, password, storage, cb) { preconditions.checkArgument(cb); preconditions.checkState(storage.hasPassphrase()); diff --git a/js/models/Storage.js b/js/models/Storage.js index 8c57e3366..936bbdc05 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -18,12 +18,14 @@ var id = 0; */ function Storage(opts) { preconditions.checkArgument(opts); - preconditions.checkArgument(opts.password); + preconditions.checkArgument(!opts.passphrase); this.wListCache = {}; this.__uniqueid = ++id; - this.passphraseConfig = opts.passphrase; - this.setPassword(opts.password); + this.passphraseConfig = opts.passphraseConfig; + + if (opts.password) + this.setPassword(opts.password); try { this.db = opts.db || localStorage; diff --git a/test/models/Identity.js b/test/Identity.js similarity index 100% rename from test/models/Identity.js rename to test/Identity.js diff --git a/test/Profile.js b/test/Profile.js index 4ac4598c1..57bd623d2 100644 --- a/test/Profile.js +++ b/test/Profile.js @@ -128,7 +128,7 @@ describe('Profile model', function() { p.addWallet('345', {}, function(err) { _.pluck(p.listWallets(), 'id').sort().should.deep.equal(['123', '234', '345']); p.deleteWallet('234', function(err) { - _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '345']); + _.pluck(p.listWallets(), 'id').sort().should.deep.equal(['123', '345']); done(); }); }) diff --git a/views/createProfile.html b/views/createProfile.html index e39eb8f01..84063cd0c 100644 --- a/views/createProfile.html +++ b/views/createProfile.html @@ -1,6 +1,5 @@
    -

    ( 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 )

    diff --git a/views/home.html b/views/home.html index 698c94ad2..100b854f6 100644 --- a/views/home.html +++ b/views/home.html @@ -1,7 +1,5 @@
    -

    ( TODO: only show this login form if there is any profile:: key) -

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

    From dd0dc9c6ad1c4c3dcd3acd703377bf4accc68ab5 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 11 Oct 2014 14:33:45 -0300 Subject: [PATCH 046/182] add warning for old wallets --- js/controllers/createProfile.js | 20 +++++++++++++++++++- js/models/Identity.js | 13 +++++++++++-- js/models/Wallet.js | 19 ++++++++++++++++--- views/createProfile.html | 11 ++++++++++- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/js/controllers/createProfile.js b/js/controllers/createProfile.js index fc72460b9..eec5e786d 100644 --- a/js/controllers/createProfile.js +++ b/js/controllers/createProfile.js @@ -2,6 +2,24 @@ angular.module('copayApp.controllers').controller('CreateProfileController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) { controllerUtils.redirIfLogged(); + $scope.retreiving = true; + + + copay.Identity.anyProfile({ + pluginManager: pluginManager, + }, function(anyProfile) { + copay.Identity.anyWallet({ + pluginManager: pluginManager, + }, function(anyWallet) { + $scope.retreiving = false; + $scope.anyProfile = anyProfile; +console.log('[createProfile.js.15:anyProfile:]',anyProfile); //TODO + $scope.anyWallet = anyWallet; +console.log('[createProfile.js.17:anyWallet:]',anyWallet); //TODO + }); + + }); + $scope.createProfile = function(form) { if (form && form.$invalid) { @@ -15,7 +33,7 @@ angular.module('copayApp.controllers').controller('CreateProfileController', fun networkName: config.networkName, walletDefaults: config.wallet, passphrase: config.passphrase, - }, function(err, iden , firstWallet) { + }, function(err, iden, firstWallet) { controllerUtils.bindProfile($scope, iden, firstWallet); }); } diff --git a/js/models/Identity.js b/js/models/Identity.js index fc9987cb6..303298343 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -105,13 +105,22 @@ Identity._getStorage = function(opts, password) { * @param opts.storageOpts * @param cb */ - - Identity.anyProfile = function(opts, cb) { var storage = Identity._getStorage(opts); Profile.any(storage,cb); }; +/** + * check if any wallet exists on storage + * + * @param opts.storageOpts + * @param cb + */ +Identity.anyWallet = function(opts, cb) { + var storage = Identity._getStorage(opts); + Wallet.any(storage,cb); +}; + /** * creates and Identity * diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 4344e6a36..92a25ad3f 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -166,6 +166,19 @@ Wallet.COPAYER_PAIR_LIMITS = { 12: 1, }; + + +Wallet.key = function(str) { + return 'wallet::' + str; +}; + + +Wallet.any = function(storage, cb) { + storage.getFirst(Wallet.key(''), function(err, val) { + return cb(val ? true : false); + }); +}; + /* for stubbing */ Wallet._newInsight = function(opts) { return new Insight(opts); @@ -217,7 +230,7 @@ Wallet.getMaxRequiredCopayers = function(totalCopayers) { Wallet.delete = function(walletId, storage, cb) { preconditions.checkArgument(cb); - storage.deletePrefix('wallet::' + walletId, function(err) { + storage.deletePrefix(Wallet.key(walletId), function(err) { if (err) return cb(err); storage.deletePrefix(walletId + '::', function(err) { return cb(err); @@ -245,7 +258,7 @@ Wallet.read = function(walletId, readOpts, cb) { err; var obj = {}; - storage.getFirst('wallet::' + walletId, function(err, ret) { + storage.getFirst(Wallet.key(walletId), function(err, ret) { if (err) return cb(err); if (!ret) @@ -1023,7 +1036,7 @@ Wallet.prototype.store = function(cb) { this.keepAlive(); var val = this.toObj(); - var key = 'wallet::' + this.id + ((val.opts && val.opts.name) ? '_' + val.opts.name : ''); + var key = Wallet.key(this.id + ((val.opts && val.opts.name) ? '_' + val.opts.name : '')); this.storage.set(key, val, function(err) { log.debug('Wallet:' + self.id + ' stored'); if (cb) diff --git a/views/createProfile.html b/views/createProfile.html index 84063cd0c..4623f4bfd 100644 --- a/views/createProfile.html +++ b/views/createProfile.html @@ -1,6 +1,15 @@
    +
    +
    + +
    +
    + Copay now needs a profile to access your wallets.
    + You can import current your wallets after creating your profile. +
    +
    + -

    ( 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... From 29bbfa184c61a8ef77c540278016964fdd0e221f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 11 Oct 2014 14:33:45 -0300 Subject: [PATCH 047/182] add warning for old wallets --- js/controllers/createProfile.js | 4 +--- js/controllers/home.js | 2 +- js/models/Profile.js | 4 ++-- js/models/Storage.js | 2 +- js/models/Wallet.js | 4 ++-- js/routes.js | 4 ---- views/createProfile.html | 2 +- views/home.html | 1 - views/open.html | 33 --------------------------------- 9 files changed, 8 insertions(+), 48 deletions(-) delete mode 100644 views/open.html diff --git a/js/controllers/createProfile.js b/js/controllers/createProfile.js index eec5e786d..20d9da77e 100644 --- a/js/controllers/createProfile.js +++ b/js/controllers/createProfile.js @@ -13,9 +13,7 @@ angular.module('copayApp.controllers').controller('CreateProfileController', fun }, function(anyWallet) { $scope.retreiving = false; $scope.anyProfile = anyProfile; -console.log('[createProfile.js.15:anyProfile:]',anyProfile); //TODO $scope.anyWallet = anyWallet; -console.log('[createProfile.js.17:anyWallet:]',anyWallet); //TODO }); }); @@ -32,7 +30,7 @@ console.log('[createProfile.js.17:anyWallet:]',anyWallet); //TODO network: config.network, networkName: config.networkName, walletDefaults: config.wallet, - passphrase: config.passphrase, + passphraseConfig: config.passphraseConfig, }, function(err, iden, firstWallet) { controllerUtils.bindProfile($scope, iden, firstWallet); }); diff --git a/js/controllers/home.js b/js/controllers/home.js index 3a3797027..0b606146e 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -24,7 +24,7 @@ angular.module('copayApp.controllers').controller('HomeController', function($sc network: config.network, networkName: config.networkName, walletDefaults: config.wallet, - passphrase: config.passphrase, + passphraseConfig: config.passphraseConfig, }, function(err, iden, firstWallet) { if (err && !iden) { console.log('Error:' + err) diff --git a/js/models/Profile.js b/js/models/Profile.js index 8f74a3373..232bf6764 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -45,8 +45,8 @@ Profile.create = function(email, password, storage, cb) { Profile.any = function(storage, cb) { - storage.getFirst(Profile.key(''), function(err, val) { - return cb(val ? true : false); + storage.getFirst(Profile.key(''), function(err, v, k) { + return cb(k ? true : false); }); }; diff --git a/js/models/Storage.js b/js/models/Storage.js index 936bbdc05..3275a1ff7 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -163,7 +163,7 @@ Storage.prototype.getFirst = function(prefix, cb) { }); if (keys.length === 0) return cb(new Error('not found')); self._read(keys[0], function(v) { - if (_.isNull(v)) return cb(new Error('Could not decrypt data')); + if (_.isNull(v)) return cb(new Error('Could not decrypt data'), null, keys[0]); return cb(null, v, keys[0]); }) }); diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 92a25ad3f..bc534aea4 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -174,8 +174,8 @@ Wallet.key = function(str) { Wallet.any = function(storage, cb) { - storage.getFirst(Wallet.key(''), function(err, val) { - return cb(val ? true : false); + storage.getFirst(Wallet.key(''), function(err, v, k) { + return cb(k ? true : false); }); }; diff --git a/js/routes.js b/js/routes.js index 2f4ce3f49..140626356 100644 --- a/js/routes.js +++ b/js/routes.js @@ -14,10 +14,6 @@ angular templateUrl: 'views/createProfile.html', validate: false }) - .when('/open', { - templateUrl: 'views/open.html', - validate: false - }) .when('/join', { templateUrl: 'views/join.html', validate: true diff --git a/views/createProfile.html b/views/createProfile.html index 4623f4bfd..3da07d81a 100644 --- a/views/createProfile.html +++ b/views/createProfile.html @@ -4,7 +4,7 @@
    - Copay now needs a profile to access your wallets.
    + Copay now needs a profile to access wallets.
    You can import current your wallets after creating your profile.
    diff --git a/views/home.html b/views/home.html index 100b854f6..80400d903 100644 --- a/views/home.html +++ b/views/home.html @@ -1,6 +1,5 @@
    -

    ( TODO: 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... diff --git a/views/open.html b/views/open.html deleted file mode 100644 index 2d3ef85a9..000000000 --- a/views/open.html +++ /dev/null @@ -1,33 +0,0 @@ -
    -
    - - Retreiving information from storage... -
    -
    - - Connecting... -
    -
    -
    - Copay -
    -
    -
    -
    -

    Open Wallet

    -
    - - -
    - « Back - -
    -
    -
    -
    -
    -
    - - From d9cd2e95d435805b8b0b2245e3a6b95ad30f164d Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sat, 11 Oct 2014 17:26:10 -0300 Subject: [PATCH 048/182] add manage page to create/join/import/delete wallets --- js/controllers/manage.js | 3 +++ js/models/Storage.js | 4 +++- js/routes.js | 4 ++++ views/includes/sidebar.html | 10 ++-------- views/manage.html | 24 ++++++++++++++++++++++++ 5 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 js/controllers/manage.js create mode 100644 views/manage.html diff --git a/js/controllers/manage.js b/js/controllers/manage.js new file mode 100644 index 000000000..821804c90 --- /dev/null +++ b/js/controllers/manage.js @@ -0,0 +1,3 @@ +'use strict'; +angular.module('copayApp.controllers').controller('ManageController', function($scope, $rootScope, $location, controllerUtils) { +}); diff --git a/js/models/Storage.js b/js/models/Storage.js index 3275a1ff7..949fc904f 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -202,7 +202,9 @@ Storage.prototype.clearAll = function(cb) { this.db.clear(cb); }; -Storage.prototype.decrypt = function(base64) { +Storage.prototype.decrypt = function(base64, password) { + // password + var decryptedStr = this._decrypt(base64); return JSON.parse(decryptedStr); }; diff --git a/js/routes.js b/js/routes.js index 140626356..21dd6e548 100644 --- a/js/routes.js +++ b/js/routes.js @@ -60,6 +60,10 @@ angular templateUrl: 'views/warning.html', validate: true }) + .when('/manage', { + templateUrl: 'views/manage.html', + validate: true + }) .otherwise({ templateUrl: 'views/errors/404.html', title: 'Error' diff --git a/views/includes/sidebar.html b/views/includes/sidebar.html index 5507316b5..83e20b5ec 100644 --- a/views/includes/sidebar.html +++ b/views/includes/sidebar.html @@ -58,16 +58,10 @@
    +
  • - - -
  • {{'Close'|translate}} diff --git a/views/manage.html b/views/manage.html new file mode 100644 index 000000000..a110f1080 --- /dev/null +++ b/views/manage.html @@ -0,0 +1,24 @@ +
    +

    Manage Wallets

    +
    +

    Backup

    +

    It's important to backup your profile so that you can recover it in case of disaster. The backup will include all your profile's wallets

    + +
    +
    + +
    From f1ae8f9c331ffbbef5aa543f3e369e43e604da8e Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 14 Oct 2014 09:58:12 -0300 Subject: [PATCH 049/182] add store/restore passphrasse methods --- js/models/Identity.js | 7 +++++-- js/models/Storage.js | 18 +++++++++++++++++- test/Identity.js | 10 ++++++++-- test/Storage.js | 2 +- test/mocks/FakeLocalStorage.js | 2 +- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 303298343..7b510ad3b 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -295,14 +295,17 @@ Identity.prototype.close = function(cb) { * @return {Wallet} */ Identity.prototype.importWallet = function(base64, password, skipFields, cb) { + preconditions.checkArgument(password); preconditions.checkArgument(cb); + this.storage.savePassphrase(); this.storage.setPassword(password); - var obj = this.storage.decrypt(base64); - if (!obj) return false; + this.storage.restorePassphrase(); + if (!obj) return false; var w = Identity._walletFromObj(obj, this.storage, this.networkOpts, this.blockchainOpts); +console.log('[Identity.js.307:Identity:]',w); //TODO this._checkVersion(w.version); this.addWallet(w, function(err) { if (err) return cb(err); diff --git a/js/models/Storage.js b/js/models/Storage.js index 949fc904f..f4392cbaf 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -45,8 +45,24 @@ Storage.prototype._getPassphrase = function() { throw new Error('NOPASSPHRASE: No passphrase set'); return pps[this.__uniqueid]; -} +}; +Storage.prototype.savePassphrase = function() { + if (!pps[this.__uniqueid]) + throw new Error('NOPASSPHRASE: No passphrase set'); + + this.savedPassphrase = this.savedPassphrase || {}; + this.savedPassphrase[this.__uniqueid] = pps[this.__uniqueid]; +}; + + +Storage.prototype.restorePassphrase = function() { + if (!this.savedPassphrase[this.__uniqueid]) + throw new Error('NOSTOREDPASSPHRASE: No stored passphrase'); + + pps[this.__uniqueid] = this.savedPassphrase[this.__uniqueid]; + this.savedPassphrase[this.__uniqueid] = undefined; +}; Storage.prototype.hasPassphrase = function() { return pps[this.__uniqueid] ? true : false; diff --git a/test/Identity.js b/test/Identity.js index 40c22e813..b75b42826 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -32,6 +32,8 @@ describe('Identity model', function() { beforeEach(function(done) { storage = sinon.stub(); storage.getItem = sinon.stub(); + storage.savePassphrase = sinon.spy(); + storage.restorePassphrase = sinon.spy(); storage.setPassword = sinon.spy(); storage.hasPassphrase = sinon.stub().returns(true); storage.getSessionId = sinon.spy(); @@ -127,7 +129,7 @@ describe('Identity model', function() { describe('#open', function(done) { beforeEach(function() { - storage.getFirst = sinon.stub().yields('wallet1234'); + storage.getFirst = sinon.stub().yields(null, 'wallet1234'); profile.listWallets = sinon.stub().returns([{id:'walletid'}]); Identity._openProfile = sinon.stub().callsArgWith(3, null, profile); Identity._walletRead = sinon.stub().callsArgWith(2, null, wallet); @@ -230,7 +232,7 @@ describe('Identity model', function() { beforeEach(function() { iden.migrateWallet = sinon.stub().yields(null); storage.setPassword = sinon.spy(); - storage.getFirst = sinon.stub().yields('wallet1234'); + storage.getFirst = sinon.stub().yields(null, 'wallet1234'); var wallet = sinon.stub(); wallet.store = sinon.stub().yields(null); @@ -256,6 +258,7 @@ describe('Identity model', function() { beforeEach(function() { iden.migrateWallet = sinon.stub().yields(null); + storage.getFirst = sinon.stub().yields(null, 'wallet1234'); }); it('should create wallet from encrypted object', function(done) { @@ -266,9 +269,12 @@ describe('Identity model', function() { wallet.getId = sinon.stub().returns('ID123'); Identity._walletFromObj = sinon.stub().returns(wallet); + Identity._walletRead = sinon.stub().yields(null,wallet); iden.importWallet("encrypted object", "xxx", [], function(err) { iden.openWallet('ID123', function(err, w) { + iden.storage.savePassphrase.calledOnce.should.equal(true); + iden.storage.restorePassphrase.calledOnce.should.equal(true); should.not.exist(err); should.exist(w); done(); diff --git a/test/Storage.js b/test/Storage.js index 842169528..a9545cfd7 100644 --- a/test/Storage.js +++ b/test/Storage.js @@ -22,7 +22,7 @@ describe('Storage model', function() { var s2 = new Storage(requireMock('FakeLocalStorage').storageParams); (function() { var params = _.clone(requireMock('FakeLocalStorage').storageParams); - params.password = undefined; + params.passphrase = '1234'; new Storage(params); }).should.throw('Illegal Argument'); }); diff --git a/test/mocks/FakeLocalStorage.js b/test/mocks/FakeLocalStorage.js index 3a8428f43..7af20d569 100644 --- a/test/mocks/FakeLocalStorage.js +++ b/test/mocks/FakeLocalStorage.js @@ -32,7 +32,7 @@ module.exports.storageParams = { password: '123', db: new FakeLocalStorage(), sessionStorage: new FakeLocalStorage(), - passphrase: { + passphraseConfig: { iterations: 1, }, }; From b3c1447376dc99ef087d6478c9243e17d9b8bf50 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Fri, 3 Oct 2014 09:56:35 -0300 Subject: [PATCH 050/182] Store secret number --- js/models/Wallet.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/js/models/Wallet.js b/js/models/Wallet.js index bc534aea4..3489df273 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -81,7 +81,7 @@ function Wallet(opts) { this.id = opts.id || Wallet.getRandomId(); - this.secretNumber = opts.secretNumber || Wallet.getRandomNumber(); + this.secretNumber = opts.secretNumber || Wallet.getRandomSecretNumber(); this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin); this.settings = opts.settings || copayConfig.wallet.settings; this.name = opts.name; @@ -149,6 +149,7 @@ Wallet.PERSISTED_PROPERTIES = [ 'addressBook', 'backupOffered', 'lastTimestamp', + 'secretNumber', ]; Wallet.COPAYER_PAIR_LIMITS = { @@ -200,11 +201,11 @@ Wallet.getRandomId = function() { }; /** - * @desc Get a random 8 byte number and encode it as a hexa string - * @return {string} + * @desc Retrieve a random secret number to secure wallet secret + * @return {string} 5 bytes, hexa encoded */ -Wallet.getRandomNumber = function() { - var r = bitcore.SecureRandom.getPseudoRandomBuffer(5).toString('hex'); +Wallet.getRandomSecretNumber = function() { + var r = bitcore.SecureRandom.getPseudoRandomBuffer(5).toString('hex') return r; }; @@ -828,8 +829,6 @@ Wallet.prototype.getMyCopayerNickname = function() { * @return {string} my own pubkey, base58 encoded */ Wallet.prototype.getSecretNumber = function() { - if (this.secretNumber) return this.secretNumber; - this.secretNumber = Wallet.getRandomNumber(); return this.secretNumber; }; @@ -1066,6 +1065,7 @@ Wallet.prototype.toObj = function() { privateKey: this.privateKey ? this.privateKey.toObj() : undefined, addressBook: this.addressBook, lastTimestamp: this.lastTimestamp || 0, + secretNumber: this.secretNumber, }; return walletObj; @@ -1131,6 +1131,8 @@ Wallet.fromObj = function(o, readOpts) { }); } + opts.secretNumber = o.secretNumber; + if (o.publicKeyRing) { opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing); } else { From 279ab41b6ca2744bea1c20efb998499ea5dc0fda Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 14 Oct 2014 12:45:32 -0300 Subject: [PATCH 051/182] call netStart() when opening wallet --- js/models/Identity.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 7b510ad3b..e937a801b 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -107,7 +107,7 @@ Identity._getStorage = function(opts, password) { */ Identity.anyProfile = function(opts, cb) { var storage = Identity._getStorage(opts); - Profile.any(storage,cb); + Profile.any(storage, cb); }; /** @@ -118,7 +118,7 @@ Identity.anyProfile = function(opts, cb) { */ Identity.anyWallet = function(opts, cb) { var storage = Identity._getStorage(opts); - Wallet.any(storage,cb); + Wallet.any(storage, cb); }; /** @@ -305,7 +305,7 @@ Identity.prototype.importWallet = function(base64, password, skipFields, cb) { if (!obj) return false; var w = Identity._walletFromObj(obj, this.storage, this.networkOpts, this.blockchainOpts); -console.log('[Identity.js.307:Identity:]',w); //TODO + console.log('[Identity.js.307:Identity:]', w); //TODO this._checkVersion(w.version); this.addWallet(w, function(err) { if (err) return cb(err); @@ -479,6 +479,7 @@ Identity.prototype.openWallet = function(walletId, cb) { w.store(function(err) { self.profile.setLastOpenedTs(walletId, function() { + w.netStart(); return cb(err, w); }); }); @@ -488,7 +489,7 @@ Identity.prototype.openWallet = function(walletId, cb) { Identity.prototype.getOpenWallet = function(id) { - return _.findWhere(this.openWallets, { + return _.findWhere(this.openWallets, { id: id, }); }; From 8b45d132a2e251d6316e2c6fb1670bd077cee8b6 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 14 Oct 2014 13:22:28 -0300 Subject: [PATCH 052/182] remove unused params --- js/models/Identity.js | 2 +- js/models/Profile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index e937a801b..f63661241 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -495,7 +495,7 @@ Identity.prototype.getOpenWallet = function(id) { }; -Identity.prototype.listWallets = function(a) { +Identity.prototype.listWallets = function() { var ret = this.profile.listWallets(); return ret; }; diff --git a/js/models/Profile.js b/js/models/Profile.js index 232bf6764..e81ef8aad 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -74,7 +74,7 @@ Profile.prototype.getWallet = function(walletId, cb) { return this.walletInfos[walletId]; }; -Profile.prototype.listWallets = function(opts, cb) { +Profile.prototype.listWallets = function() { return _.sortBy(this.walletInfos, function(winfo) { return -winfo.lastOpenedTs || -winfo.createdTs; }); From a3d96535cdd08ebdd70d13a143a565478d0422da Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 14 Oct 2014 15:39:07 -0300 Subject: [PATCH 053/182] rebased to current master --- test/Identity.js | 1 + test/Profile.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Identity.js b/test/Identity.js index b75b42826..2e22a7c3d 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -235,6 +235,7 @@ describe('Identity model', function() { storage.getFirst = sinon.stub().yields(null, 'wallet1234'); var wallet = sinon.stub(); + wallet.netStart = sinon.stub(); wallet.store = sinon.stub().yields(null); Identity._walletRead = sinon.stub().callsArgWith(2, null, wallet); diff --git a/test/Profile.js b/test/Profile.js index 57bd623d2..fe243c1c2 100644 --- a/test/Profile.js +++ b/test/Profile.js @@ -112,10 +112,12 @@ describe('Profile model', function() { it('should list wallets in order', function(done) { var p = new Profile(opts, storage); p.addWallet('123', {}, function(err) { + setTimeout(function() { p.addWallet('234', {}, function(err) { - _.pluck(p.listWallets(), 'id').should.deep.equal(['123', '234']); + _.pluck(p.listWallets(), 'id').should.deep.equal(['234', '123']); done(); }) + },10); }); }); }); From b391344dafa3773dfc78874358d0f9ce34724f18 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 14 Oct 2014 19:49:29 -0300 Subject: [PATCH 054/182] fix join flow --- js/controllers/copayers.js | 2 +- js/models/Async.js | 3 +++ js/models/Identity.js | 6 ++++-- js/models/Wallet.js | 33 ++++++++++++++++++--------------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/js/controllers/copayers.js b/js/controllers/copayers.js index 8896e7566..5ee6add64 100644 --- a/js/controllers/copayers.js +++ b/js/controllers/copayers.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('CopayersController', - function($scope, $rootScope, $location, backupService, identity, controllerUtils) { + function($scope, $rootScope, $location, backupService, controllerUtils) { $scope.isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; $scope.hideAdv = true; diff --git a/js/models/Async.js b/js/models/Async.js index 60f089030..5d5b5484d 100644 --- a/js/models/Async.js +++ b/js/models/Async.js @@ -294,11 +294,13 @@ Network.prototype.greet = function(copayerId, secretNumber) { }; Network.prototype._addCopayerMap = function(peerId, copayerId) { +console.log('[Async.js.296:_addCopayerMap:]',peerId, copayerId); //TODO if (!this.copayerForPeer[peerId]) { if (Object.keys(this.copayerForPeer).length < this.maxPeers) { this.copayerForPeer[peerId] = copayerId; } } +console.log('[Async.js.296:_addCopayerMap:]',this.copayerForPeer); //TODO }; Network.prototype._setInboundPeerAuth = function(peerId) { @@ -374,6 +376,7 @@ Network.prototype.send = function(dest, payload, cb) { dest = this.getCopayerIds(); payload.isBroadcast = 1; } +console.log('[Async.js.374:dest:]',dest); //TODO if (typeof dest === 'string') dest = [dest]; diff --git a/js/models/Identity.js b/js/models/Identity.js index f63661241..d2dcdd335 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -39,6 +39,7 @@ function Identity(email, password, opts) { this.walletDefaults = opts.walletDefaults || {}; this.version = opts.version || version; + this.email = email; // open wallets this.openWallets = []; @@ -376,7 +377,7 @@ Identity.prototype.createWallet = function(opts, cb) { }); opts.publicKeyRing.addCopayer( opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(), - opts.nickname + opts.nickname || this.email ); log.debug('\t### PublicKeyRing Initialized'); @@ -605,9 +606,10 @@ Identity.prototype.joinWallet = function(opts, cb) { var walletOpts = _.clone(data.opts); walletOpts.id = data.walletId; + walletOpts.network = joinNetwork; walletOpts.privateKey = privateKey; - walletOpts.nickname = opts.nickname || self.profile.name; + walletOpts.nickname = opts.nickname || this.email; if (opts.password) walletOpts.password = opts.password; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 3489df273..0b0243ccf 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -380,6 +380,7 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) { var inPKR = PublicKeyRing.fromObj(data.publicKeyRing); var wasIncomplete = !this.publicKeyRing.isComplete(); +console.log('[Wallet.js.382:wasIncomplete:]',wasIncomplete); //TODO var hasChanged; try { @@ -389,7 +390,7 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) { this.emit('connectionError', e.message); return; } - +console.log('[Wallet.js.393:hasChanged:]',hasChanged); //TODO if (hasChanged) { if (wasIncomplete) { this.sendPublicKeyRing(); @@ -699,10 +700,7 @@ Wallet.prototype._onData = function(senderId, data, ts) { break; case 'walletReady': - console.log('[Wallet.js.653]', this.lastMessageFrom[senderId]); //TODO if (this.lastMessageFrom[senderId] !== 'walletReady') { - - console.log('[Wallet.js.656]'); //TODO log.debug('Wallet:' + this.id + ' peer Sync received. since: ' + (data.sinceTs || 0)); this.sendPublicKeyRing(senderId); this.sendAddressBook(senderId); @@ -927,16 +925,27 @@ Wallet.prototype.netStart = function() { var self = this; var net = this.network; + net.removeAllListeners(); + net.on('connect', self._onConnect.bind(self)); + net.on('data', self._onData.bind(self)); + net.on('no messages', self._onNoMessages.bind(self)); + net.on('connect_error', function() { + self.emit('connectionError'); + }); + + if (this.publicKeyRing.isComplete()) { + this._lockIncomming(); + } + + + if (net.started) { log.debug('Wallet:' + self.id + ' Wallet networking was ready') self.emit('ready', net.getPeer()); return; } - net.removeAllListeners(); - net.on('connect', self._onConnect.bind(self)); - net.on('data', self._onData.bind(self)); - net.on('no messages', self._onNoMessages.bind(self)); + var myId = self.getMyCopayerId(); var myIdPriv = self.getMyCopayerIdPriv(); @@ -949,13 +958,6 @@ Wallet.prototype.netStart = function() { secretNumber: self.secretNumber, }; - if (this.publicKeyRing.isComplete()) { - this._lockIncomming(); - } - - net.on('connect_error', function() { - self.emit('connectionError'); - }); log.debug('Wallet:' + self.id + ' Starting networking: ' + startOpts.copayerId); net.start(startOpts, function() { @@ -1303,6 +1305,7 @@ Wallet.prototype.sendIndexes = function(recipients) { * @param {string[]} recipients - the pubkeys of the recipients */ Wallet.prototype.sendAddressBook = function(recipients) { + if ( !Object.keys(this.addressBook).length ) return; log.debug('Wallet:' + this.id + ' ### SENDING addressBook TO:', recipients || 'All', this.addressBook); this.send(recipients, { type: 'addressbook', From a3d0b9e48bcd6632f94f3509531e31a560241e04 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 15 Oct 2014 08:52:22 -0300 Subject: [PATCH 055/182] fix manage controller, refactor logged attr --- js/routes.js | 81 +++++++++++++++++++++++------------------------ views/manage.html | 4 +-- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/js/routes.js b/js/routes.js index 21dd6e548..4ab9abed8 100644 --- a/js/routes.js +++ b/js/routes.js @@ -8,47 +8,9 @@ angular $routeProvider .when('/', { templateUrl: 'views/home.html', - validate: false }) .when('/createProfile', { templateUrl: 'views/createProfile.html', - validate: false - }) - .when('/join', { - templateUrl: 'views/join.html', - validate: true - }) - .when('/import', { - templateUrl: 'views/import.html', - validate: false - }) - .when('/create', { - templateUrl: 'views/create.html', - validate: true - }) - .when('/copayers', { - templateUrl: 'views/copayers.html', - validate: true - }) - .when('/receive', { - templateUrl: 'views/addresses.html', - validate: true - }) - .when('/history', { - templateUrl: 'views/transactions.html', - validate: true - }) - .when('/send', { - templateUrl: 'views/send.html', - validate: true - }) - .when('/more', { - templateUrl: 'views/more.html', - validate: true - }) - .when('/settings', { - templateUrl: 'views/settings.html', - validate: false }) .when('/unsupported', { templateUrl: 'views/unsupported.html' @@ -56,13 +18,49 @@ angular .when('/uri-payment/:data', { templateUrl: 'views/uri-payment.html' }) + .when('/join', { + templateUrl: 'views/join.html', + logged: true + }) + .when('/import', { + templateUrl: 'views/import.html', + logged: true + }) + .when('/create', { + templateUrl: 'views/create.html', + logged: true + }) + .when('/copayers', { + templateUrl: 'views/copayers.html', + logged: true + }) + .when('/receive', { + templateUrl: 'views/addresses.html', + logged: true + }) + .when('/history', { + templateUrl: 'views/transactions.html', + logged: true + }) + .when('/send', { + templateUrl: 'views/send.html', + logged: true + }) + .when('/more', { + templateUrl: 'views/more.html', + logged: true + }) + .when('/settings', { + templateUrl: 'views/settings.html', + logged: true + }) .when('/warning', { templateUrl: 'views/warning.html', - validate: true + logged: true }) .when('/manage', { templateUrl: 'views/manage.html', - validate: true + logged: true }) .otherwise({ templateUrl: 'views/errors/404.html', @@ -90,7 +88,8 @@ angular if (!localStorage || localStorage.length < 1) { $location.path('unsupported'); } else { - if ((!$rootScope.wallet || !$rootScope.wallet.id) && next.validate) { + if (!$rootScope.iden && next.logged) { + console.log('not logged... redirecting') $idle.unwatch(); $location.path('/'); } diff --git a/views/manage.html b/views/manage.html index a110f1080..9b616ecd0 100644 --- a/views/manage.html +++ b/views/manage.html @@ -1,7 +1,7 @@ -
    +

    Manage Wallets

    -

    Backup

    +

    Manage Wallets

    It's important to backup your profile so that you can recover it in case of disaster. The backup will include all your profile's wallets

    Backup profile From 95e8a22c5429f00c37e8b61dbee0ec3bb52b2aa6 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 15 Oct 2014 12:09:10 -0300 Subject: [PATCH 056/182] enhacements on notifications and balance updates --- js/controllers/more.js | 3 +- js/controllers/sidebar.js | 2 +- js/models/Async.js | 3 - js/models/Identity.js | 5 +- js/models/Insight.js | 2 + js/models/Wallet.js | 13 ++-- js/services/controllerUtils.js | 119 ++++++++++++++++----------------- 7 files changed, 73 insertions(+), 74 deletions(-) diff --git a/js/controllers/more.js b/js/controllers/more.js index b867b2aae..b25e33288 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -84,7 +84,8 @@ angular.module('copayApp.controllers').controller('MoreController', $scope.deleteWallet = function() { $rootScope.iden.deleteWallet(w.id, function() { - controllerUtils.logout(); + notification.info('Wallet deleted', $filter('translate')('wallet deleted')); + $location.path('/manage'); }); }; diff --git a/js/controllers/sidebar.js b/js/controllers/sidebar.js index 971de1820..853353905 100644 --- a/js/controllers/sidebar.js +++ b/js/controllers/sidebar.js @@ -39,7 +39,7 @@ angular.module('copayApp.controllers').controller('SidebarController', function( if (w.isReady()) { w.sendWalletReady(); if ($rootScope.addrInfos.length > 0) { - controllerUtils.updateBalance(function() { + controllerUtils.updateBalance(w, function() { $rootScope.$digest(); }); } diff --git a/js/models/Async.js b/js/models/Async.js index 5d5b5484d..60f089030 100644 --- a/js/models/Async.js +++ b/js/models/Async.js @@ -294,13 +294,11 @@ Network.prototype.greet = function(copayerId, secretNumber) { }; Network.prototype._addCopayerMap = function(peerId, copayerId) { -console.log('[Async.js.296:_addCopayerMap:]',peerId, copayerId); //TODO if (!this.copayerForPeer[peerId]) { if (Object.keys(this.copayerForPeer).length < this.maxPeers) { this.copayerForPeer[peerId] = copayerId; } } -console.log('[Async.js.296:_addCopayerMap:]',this.copayerForPeer); //TODO }; Network.prototype._setInboundPeerAuth = function(peerId) { @@ -376,7 +374,6 @@ Network.prototype.send = function(dest, payload, cb) { dest = this.getCopayerIds(); payload.isBroadcast = 1; } -console.log('[Async.js.374:dest:]',dest); //TODO if (typeof dest === 'string') dest = [dest]; diff --git a/js/models/Identity.js b/js/models/Identity.js index d2dcdd335..2fb9d102a 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -200,9 +200,10 @@ Identity.open = function(email, password, opts, cb) { var firstWallet; _.each(wids, function(wid) { iden.openWallet(wid, function(err, w) { - if (err) + if (err) { log.error('Cound not open wallet id:' + wid + '. Skipping') - else { + iden.profile.deleteWallet(wid, function() {}); + } else { log.info('Open wallet id:' + wid + ' opened'); if (!firstWallet) firstWallet = w; diff --git a/js/models/Insight.js b/js/models/Insight.js index 8847c7f85..50a22dfc0 100644 --- a/js/models/Insight.js +++ b/js/models/Insight.js @@ -223,6 +223,8 @@ Insight.prototype.subscribe = function(addresses) { s.emit('subscribe', address); s.on(address, handler); + } else { + log.debug('Already subcribed to: ', address); } }); }; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 0b0243ccf..8d9facd46 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -315,6 +315,7 @@ Wallet.prototype.seedCopayer = function(pubKey) { Wallet.prototype._newAddresses = function(dontUpdateUx) { if (this.publicKeyRing.isComplete()) { this.subscribeToAddresses(); + }; this.emit('newAddresses', dontUpdateUx); }; @@ -380,7 +381,6 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) { var inPKR = PublicKeyRing.fromObj(data.publicKeyRing); var wasIncomplete = !this.publicKeyRing.isComplete(); -console.log('[Wallet.js.382:wasIncomplete:]',wasIncomplete); //TODO var hasChanged; try { @@ -390,7 +390,6 @@ console.log('[Wallet.js.382:wasIncomplete:]',wasIncomplete); //TODO this.emit('connectionError', e.message); return; } -console.log('[Wallet.js.393:hasChanged:]',hasChanged); //TODO if (hasChanged) { if (wasIncomplete) { this.sendPublicKeyRing(); @@ -881,12 +880,11 @@ Wallet.prototype._setBlockchainListeners = function() { var self = this; self.blockchain.removeAllListeners(); + + log.debug('Setting Blockchain listeners for', this.getId()); self.blockchain.on('reconnect', function(attempts) { log.debug('Wallet:' + self.id + 'blockchain reconnect event'); self.emit('insightReconnected'); - - // Subscription should persist? TODO - //self.subscribeToAddresses(); }); self.blockchain.on('disconnect', function() { @@ -894,7 +892,7 @@ Wallet.prototype._setBlockchainListeners = function() { self.emit('insightError'); }); self.blockchain.on('tx', function(tx) { - log.debug('Wallet:' + self.id + 'blockchain tx event'); + log.debug('Wallet:' + self.id + ' blockchain tx event'); var addresses = self.getAddressesInfo(); var addr = _.findWhere(addresses, { addressStr: tx.address @@ -1305,7 +1303,7 @@ Wallet.prototype.sendIndexes = function(recipients) { * @param {string[]} recipients - the pubkeys of the recipients */ Wallet.prototype.sendAddressBook = function(recipients) { - if ( !Object.keys(this.addressBook).length ) return; + if (!Object.keys(this.addressBook).length) return; log.debug('Wallet:' + this.id + ' ### SENDING addressBook TO:', recipients || 'All', this.addressBook); this.send(recipients, { type: 'addressbook', @@ -2221,6 +2219,7 @@ Wallet.prototype.getAddressesStr = function(opts) { Wallet.prototype.subscribeToAddresses = function() { var addrInfo = this.publicKeyRing.getAddressesInfo(); this.blockchain.subscribe(_.pluck(addrInfo, 'addressStr')); + log.debug('Subscribed to ' + addrInfo.length + ' addresses'); //TODO }; /** diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index eee1e9efa..8e44406b4 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -11,7 +11,7 @@ angular.module('copayApp.services') if (w) { if (!w.isReady()) { $location.path('/copayers'); - } + } } else { $location.path('/'); } @@ -63,6 +63,16 @@ angular.module('copayApp.services') return wid === $rootScope.wallet.getId(); }; + + root.updateTxsAndBalance = _.debounce(function(w) { + root.updateTxs({ + wallet: w + }); + root.updateBalance(w, function() { + $rootScope.$digest(); + }) + }, 3000); + root.installWalletHandlers = function($scope, w) { w.removeAllListeners(); @@ -94,41 +104,33 @@ angular.module('copayApp.services') }); w.on('newAddresses', function(dontDigest) { - if (root.isFocusedWallet(wid)) { - root.updateAddressList(); - if (!dontDigest) { - $rootScope.$digest(); - } + root.updateTxsAndBalance(w); + if (!dontDigest) { + $rootScope.$digest(); } }); w.on('tx', function(address, isChange) { - if (root.isFocusedWallet(wid)) { - if (!isChange) { - notification.funds('Funds received!', address); - } - root.updateBalance(function() { - $rootScope.$digest(); - }); + if (!isChange) { + notification.funds('Funds received on ' + w.getName(), address); } + root.updateBalance(w, function() { + $rootScope.$digest(); + }); }); w.on('balanceUpdated', function() { - if (root.isFocusedWallet(wid)) { - root.updateBalance(function() { - $rootScope.$digest(); - }); - } + root.updateBalance(w, function() { + $rootScope.$digest(); + }); }); w.on('insightReconnected', function() { - if (root.isFocusedWallet(wid)) { - $rootScope.reconnecting = false; - root.updateAddressList(); - root.updateBalance(function() { - $rootScope.$digest(); - }); - } + $rootScope.reconnecting = false; + root.updateAddressList(w.getId()); + root.updateBalance(w, function() { + $rootScope.$digest(); + }); }); w.on('insightError', function() { @@ -138,17 +140,8 @@ angular.module('copayApp.services') } }); - var updateTxsAndBalance = _.debounce(function() { - root.updateTxs(); - root.updateBalance(function() { - $rootScope.$digest(); - }) - }, 3000); - w.on('txProposalsUpdated', function(dontDigest) { - if (root.isFocusedWallet(wid)) { - updateTxsAndBalance(); - } + root.updateTxsAndBalance(w); }); w.on('txProposalEvent', function(e) { @@ -212,12 +205,12 @@ angular.module('copayApp.services') }; root.setFocusedWallet = function(w) { - if (!_.isObject(w) ) + if (!_.isObject(w)) w = $rootScope.iden.getOpenWallet(w); preconditions.checkState(w && _.isObject(w)); $rootScope.wallet = w; - root.updateAddressList(); + root.updateTxsAndBalance(); root.redirIfLogged(); }; @@ -241,10 +234,11 @@ angular.module('copayApp.services') } }; - root.updateBalance = function(cb) { - var w = $rootScope.wallet; + root.updateBalance = function(w, cb) { + w = w || $rootScope.wallet; if (!w) return root.onErrorDigest(); if (!w.isReady()) return; + console.log('## Updating balance of:' + w.id) $rootScope.balanceByAddr = {}; $rootScope.updatingBalance = true; @@ -255,34 +249,39 @@ angular.module('copayApp.services') var satToUnit = 1 / w.settings.unitToSatoshi; var COIN = bitcore.util.COIN; - $rootScope.totalBalance = balanceSat * satToUnit; - $rootScope.totalBalanceBTC = (balanceSat / COIN); - $rootScope.availableBalance = safeBalanceSat * satToUnit; - $rootScope.availableBalanceBTC = (safeBalanceSat / COIN); + if (root.isFocusedWallet(w.getId())) { + $rootScope.totalBalance = balanceSat * satToUnit; + $rootScope.totalBalanceBTC = (balanceSat / COIN); + $rootScope.availableBalance = safeBalanceSat * satToUnit; + $rootScope.availableBalanceBTC = (safeBalanceSat / COIN); - $rootScope.lockedBalance = (balanceSat - safeBalanceSat) * satToUnit; - $rootScope.lockedBalanceBTC = (balanceSat - safeBalanceSat) / COIN; + $rootScope.lockedBalance = (balanceSat - safeBalanceSat) * satToUnit; + $rootScope.lockedBalanceBTC = (balanceSat - safeBalanceSat) / COIN; - var balanceByAddr = {}; - for (var ii in balanceByAddrSat) { - balanceByAddr[ii] = balanceByAddrSat[ii] * satToUnit; + var balanceByAddr = {}; + for (var ii in balanceByAddrSat) { + balanceByAddr[ii] = balanceByAddrSat[ii] * satToUnit; + } + $rootScope.balanceByAddr = balanceByAddr; + root.updateAddressList(); + $rootScope.updatingBalance = false; + + rateService.whenAvailable(function() { + $rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, w.settings.alternativeIsoCode); + $rootScope.alternativeIsoCode = w.settings.alternativeIsoCode; + $rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, w.settings.alternativeIsoCode); + $rootScope.alternativeConversionRate = rateService.toFiat(100000000, w.settings.alternativeIsoCode); + return cb ? cb() : null; + }); + } else { + // TODO + console.log('TODO: balance updated of a unfocused wallet'); } - $rootScope.balanceByAddr = balanceByAddr; - root.updateAddressList(); - $rootScope.updatingBalance = false; - - rateService.whenAvailable(function() { - $rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, w.settings.alternativeIsoCode); - $rootScope.alternativeIsoCode = w.settings.alternativeIsoCode; - $rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, w.settings.alternativeIsoCode); - $rootScope.alternativeConversionRate = rateService.toFiat(100000000, w.settings.alternativeIsoCode); - return cb ? cb() : null; - }); }); }; root.updateTxs = function(opts) { - var w = $rootScope.wallet; + var w = opts.wallet || $rootScope.wallet; if (!w) return; opts = opts || $rootScope.txsOpts || {}; From aa9f16a7e966c08882053974c054723e51954a1d Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Wed, 15 Oct 2014 10:59:09 -0300 Subject: [PATCH 057/182] on identity open return most recently used wallet --- js/models/Identity.js | 14 ++++++---- test/Identity.js | 64 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 2fb9d102a..95097eefd 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -197,7 +197,8 @@ Identity.open = function(email, password, opts, cb) { // Open All wallets from profile //This could be optional, or opts.onlyOpen = wid - var firstWallet; + var wallets = []; + var remaining = wids.length; _.each(wids, function(wid) { iden.openWallet(wid, function(err, w) { if (err) { @@ -205,13 +206,16 @@ Identity.open = function(email, password, opts, cb) { iden.profile.deleteWallet(wid, function() {}); } else { log.info('Open wallet id:' + wid + ' opened'); - if (!firstWallet) - firstWallet = w; + wallets.push(w); + } + if (--remaining == 0) { + var firstWallet = _.findWhere(wallets, { + id: wids[0] + }); + return cb(err, iden, firstWallet); } }) }); - - return cb(err, iden, firstWallet); }); }; diff --git a/test/Identity.js b/test/Identity.js index 2e22a7c3d..7098a3572 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -54,11 +54,11 @@ describe('Identity model', function() { profile.listWallets = sinon.stub().returns([]); profile.setLastOpenedTs = sinon.stub().yields(null);; profile.store = sinon.stub().yields(null);; - Identity._createProfile = sinon.stub().callsArgWith(3,null,profile); + Identity._createProfile = sinon.stub().callsArgWith(3, null, profile); - Identity.create(email, password, config, function(err,i){ + Identity.create(email, password, config, function(err, i) { iden = i; done(); }); @@ -130,9 +130,11 @@ describe('Identity model', function() { describe('#open', function(done) { beforeEach(function() { storage.getFirst = sinon.stub().yields(null, 'wallet1234'); - profile.listWallets = sinon.stub().returns([{id:'walletid'}]); + profile.listWallets = sinon.stub().returns([{ + id: 'walletid' + }]); Identity._openProfile = sinon.stub().callsArgWith(3, null, profile); - Identity._walletRead = sinon.stub().callsArgWith(2, null, wallet); + Identity._walletRead = sinon.stub().callsArgWith(2, null, wallet); }); it('should call ._openProfile', function(done) { @@ -143,6 +145,32 @@ describe('Identity model', function() { done(); }); }); + + it('should return last used wallet', function(done) { + var wallets = [{ + id: 'wallet1', + store: sinon.stub().yields(null), + netStart: sinon.stub(), + }, { + id: 'wallet2', + store: sinon.stub().yields(null), + netStart: sinon.stub(), + }, { + id: 'wallet3', + store: sinon.stub().yields(null), + netStart: sinon.stub(), + }]; + profile.listWallets = sinon.stub().returns(wallets); + Identity._walletRead = sinon.stub(); + Identity._walletRead.onCall(0).callsArgWith(2, null, wallets[0]); + Identity._walletRead.onCall(1).callsArgWith(2, null, wallets[1]); + Identity._walletRead.onCall(2).callsArgWith(2, null, wallets[2]); + + Identity.open(email, password, config, function(err, iden, w) { + w.id.should.equal('wallet1'); + done(); + }); + }); }); }); describe('#store', function() { @@ -270,7 +298,7 @@ describe('Identity model', function() { wallet.getId = sinon.stub().returns('ID123'); Identity._walletFromObj = sinon.stub().returns(wallet); - Identity._walletRead = sinon.stub().yields(null,wallet); + Identity._walletRead = sinon.stub().yields(null, wallet); iden.importWallet("encrypted object", "xxx", [], function(err) { iden.openWallet('ID123', function(err, w) { @@ -344,7 +372,9 @@ describe('Identity model', function() { networkName: 'aWeirdNetworkName', opts: {}, }); - Identity._newAsync = function() { return net; }; + Identity._newAsync = function() { + return net; + }; opts.privHex = undefined; iden.joinWallet(opts, function(err, w) { @@ -367,7 +397,9 @@ describe('Identity model', function() { type: 'walletId', networkName: iden.networkName, }); - Identity._newAsync = function() { return net; }; + Identity._newAsync = function() { + return net; + }; iden.joinWallet(opts, function(err, w) { err.should.equal('joinError'); @@ -390,7 +422,9 @@ describe('Identity model', function() { networkName: 'testnet', opts: {}, }); - Identity._newAsync = function() { return net; }; + Identity._newAsync = function() { + return net; + }; var w = sinon.stub(); w.sendWalletReady = sinon.spy(); @@ -420,7 +454,9 @@ describe('Identity model', function() { networkName: 'testnet', opts: {}, }); - Identity._newAsync = function() { return net; }; + Identity._newAsync = function() { + return net; + }; iden.createWallet = sinon.stub().yields(null, null); iden.joinWallet(opts, function(err, w) { err.should.equal('walletFull'); @@ -431,7 +467,9 @@ describe('Identity model', function() { it('should accept a priv key a input', function() { opts.privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd'; var net = sinon.stub(); - Identity._newAsync = function() { return net; }; + Identity._newAsync = function() { + return net; + }; net.on = sinon.stub(); net.cleanUp = sinon.spy(); @@ -446,12 +484,12 @@ describe('Identity model', function() { net.cleanUp = sinon.spy(); net.on = sinon.stub(); net.start = sinon.spy(); - Identity._newAsync = function() { return net; }; + Identity._newAsync = function() { + return net; + }; iden.joinWallet(opts, function(err, w) { net.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer }); }); }); }); - - From 47ae3fcb199d7a15332b8b6419f85979a1d5e260 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 15 Oct 2014 15:02:14 -0300 Subject: [PATCH 058/182] store iterations in Storage --- js/models/Identity.js | 4 ++++ js/models/Insight.js | 1 + js/models/Passphrase.js | 2 +- js/models/Storage.js | 14 +++++++++----- js/services/controllerUtils.js | 4 +++- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 95097eefd..f025ef1e1 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -332,6 +332,10 @@ Identity.prototype.closeWallet = function(wid, cb) { }); }; + +Identity.prototype.toObj = function(wid, cb) { +}; + /** * @desc This method prepares options for a new Wallet * diff --git a/js/models/Insight.js b/js/models/Insight.js index 50a22dfc0..39761a8de 100644 --- a/js/models/Insight.js +++ b/js/models/Insight.js @@ -200,6 +200,7 @@ Insight.prototype.subscribe = function(addresses) { var self = this; function handlerFor(self, address) { +console.log('HANDLER [Insight.js.150:address:]',address); //TODO return function(txid) { // verify the address is still subscribed if (!self.subscribed[address]) return; diff --git a/js/models/Passphrase.js b/js/models/Passphrase.js index 53142f0b5..120e8797a 100644 --- a/js/models/Passphrase.js +++ b/js/models/Passphrase.js @@ -20,7 +20,7 @@ function Passphrase(config) { preconditions.checkArgument(!config || !config.iterations || _.isNumber(config.iterations)); config = config || {}; this.salt = config.salt || 'mjuBtGybi/4='; - this.iterations = config.iterations || 1000; + this.iterations = config.iterations; }; /** diff --git a/js/models/Storage.js b/js/models/Storage.js index f4392cbaf..6f1e83a7b 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -52,7 +52,10 @@ Storage.prototype.savePassphrase = function() { throw new Error('NOPASSPHRASE: No passphrase set'); this.savedPassphrase = this.savedPassphrase || {}; - this.savedPassphrase[this.__uniqueid] = pps[this.__uniqueid]; + this.savedPassphrase[this.__uniqueid] = { + pps: pps[this.__uniqueid], + iterations: this.iterations, + }; }; @@ -60,7 +63,7 @@ Storage.prototype.restorePassphrase = function() { if (!this.savedPassphrase[this.__uniqueid]) throw new Error('NOSTOREDPASSPHRASE: No stored passphrase'); - pps[this.__uniqueid] = this.savedPassphrase[this.__uniqueid]; + this._setPassphrase(this.savedPassphrase[this.__uniqueid].pps, this.savedPassphrase[this.__uniqueid].iterations); this.savedPassphrase[this.__uniqueid] = undefined; }; @@ -69,15 +72,16 @@ Storage.prototype.hasPassphrase = function() { }; -Storage.prototype._setPassphrase = function(passphrase) { +Storage.prototype._setPassphrase = function(passphrase, iterations) { pps[this.__uniqueid] = passphrase; + this.iterations = iterations; }; 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('Setting passphrase... Iterations:' + passphraseConfig.iterations); + this._setPassphrase(p.getBase64(password), passphraseConfig.iterations); log.debug('done.') } diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index 8e44406b4..6f173e71a 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -210,8 +210,10 @@ angular.module('copayApp.services') preconditions.checkState(w && _.isObject(w)); $rootScope.wallet = w; - root.updateTxsAndBalance(); root.redirIfLogged(); + root.updateBalance(w, function() { + $rootScope.$digest(); + }) }; root.bindProfile = function($scope, iden, w) { From e3107c85ac1e2e39cd69e5bc7fd259a77566c419 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Wed, 15 Oct 2014 15:54:17 -0300 Subject: [PATCH 059/182] fix delete wallet --- js/models/Identity.js | 1 - js/models/Profile.js | 1 - js/models/Wallet.js | 6 +++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index f025ef1e1..e4ce528a5 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -518,7 +518,6 @@ Identity.prototype.listWallets = function() { */ Identity.prototype.deleteWallet = function(walletId, cb) { var self = this; - Identity._walletDelete(walletId, this.storage, function(err) { if (err) return cb(err); self.profile.deleteWallet(walletId, function(err) { diff --git a/js/models/Profile.js b/js/models/Profile.js index e81ef8aad..6753db231 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -86,7 +86,6 @@ Profile.prototype.deleteWallet = function(walletId, cb) { return cb(new Error('WNOEXIST: Wallet not on profile ')); delete this.walletInfos[walletId]; - this.store({ overwrite: true }, cb); diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 8d9facd46..840b8a2f9 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -230,11 +230,11 @@ Wallet.getMaxRequiredCopayers = function(totalCopayers) { */ Wallet.delete = function(walletId, storage, cb) { preconditions.checkArgument(cb); - storage.deletePrefix(Wallet.key(walletId), function(err) { - if (err) return cb(err); + if (err && err.message != 'not found') return cb(err); storage.deletePrefix(walletId + '::', function(err) { - return cb(err); + if (err && err.message != 'not found') return cb(err); + return cb(); }); }); }; From e1b9f4f859345f967cad14ea246cf2f9f58bcad0 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 15 Oct 2014 16:24:21 -0300 Subject: [PATCH 060/182] add #toEncryptedObj --- js/models/Identity.js | 17 ++++++++++++++++- js/models/Storage.js | 1 - test/Identity.js | 24 ++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index e4ce528a5..2bd2d4c7b 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -333,9 +333,24 @@ Identity.prototype.closeWallet = function(wid, cb) { }; -Identity.prototype.toObj = function(wid, cb) { +/** + * @desc Return a base64 encrypted version of the wallet + * @return {string} base64 encoded string + */ +Identity.prototype.toEncryptedObj = function() { + var ret = {}; + ret.iterations = this.storage.iterations; + ret.wallets = {}; + + _.each(this.openWallets, function(w){ + ret.wallets[w.getId()] = w.toEncryptedObj(); + }); + + return ret; }; + + /** * @desc This method prepares options for a new Wallet * diff --git a/js/models/Storage.js b/js/models/Storage.js index 6f1e83a7b..7714bb2fa 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -64,7 +64,6 @@ Storage.prototype.restorePassphrase = function() { throw new Error('NOSTOREDPASSPHRASE: No stored passphrase'); this._setPassphrase(this.savedPassphrase[this.__uniqueid].pps, this.savedPassphrase[this.__uniqueid].iterations); - this.savedPassphrase[this.__uniqueid] = undefined; }; Storage.prototype.hasPassphrase = function() { diff --git a/test/Identity.js b/test/Identity.js index 7098a3572..45f1ec141 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -353,6 +353,30 @@ describe('Identity model', function() { }); }); + + describe('#toEncryptedObj', function() { + + beforeEach(function() { + var ws = []; + _.each([0, 1, 2, 3, 4], function(i) { + var w = sinon.stub(); + w.toEncryptedObj = sinon.stub().returns('enc' + i); + w.getId = sinon.stub().returns('wid' + i); + ws.push(w); + }); + iden.openWallets = ws; + iden.storage.iterations = 13; + }); + + it('should create an encrypted object', function() { + var ret = iden.toEncryptedObj(); + ret.iterations.should.equal(13); + _.each([0, 1, 2, 3, 4], function(i) { + ret.wallets['wid' + i].should.equal('enc' + i); + }); + }); + }); + describe('#joinWallet', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', From 7a463c9a9da98acf3a24e7988a17933052da40d8 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 16 Oct 2014 10:18:37 -0300 Subject: [PATCH 061/182] wallet and profile backup working on the UX --- js/controllers/manage.js | 6 +++++- js/controllers/more.js | 2 +- js/models/Identity.js | 5 ++--- js/models/Wallet.js | 2 +- js/services/backupService.js | 42 ++++++++++++++++++++---------------- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/js/controllers/manage.js b/js/controllers/manage.js index 821804c90..20710ac7c 100644 --- a/js/controllers/manage.js +++ b/js/controllers/manage.js @@ -1,3 +1,7 @@ 'use strict'; -angular.module('copayApp.controllers').controller('ManageController', function($scope, $rootScope, $location, controllerUtils) { +angular.module('copayApp.controllers').controller('ManageController', function($scope, $rootScope, $location, controllerUtils, backupService) { + + $scope.downloadBackup = function() { + backupService.profileDownload($rootScope.iden); + }; }); diff --git a/js/controllers/more.js b/js/controllers/more.js index b25e33288..494e794ea 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -74,7 +74,7 @@ angular.module('copayApp.controllers').controller('MoreController', } $scope.downloadBackup = function() { - backupService.download(w); + backupService.walletDownload(w); } $scope.viewBackup = function() { diff --git a/js/models/Identity.js b/js/models/Identity.js index 2bd2d4c7b..2a0472ab4 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -39,7 +39,6 @@ function Identity(email, password, opts) { this.walletDefaults = opts.walletDefaults || {}; this.version = opts.version || version; - this.email = email; // open wallets this.openWallets = []; @@ -401,7 +400,7 @@ Identity.prototype.createWallet = function(opts, cb) { }); opts.publicKeyRing.addCopayer( opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(), - opts.nickname || this.email + opts.nickname || this.profile.getName() ); log.debug('\t### PublicKeyRing Initialized'); @@ -632,7 +631,7 @@ Identity.prototype.joinWallet = function(opts, cb) { walletOpts.network = joinNetwork; walletOpts.privateKey = privateKey; - walletOpts.nickname = opts.nickname || this.email; + walletOpts.nickname = opts.nickname || this.profile.getName(); if (opts.password) walletOpts.password = opts.password; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 840b8a2f9..bc86c2de1 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -1171,7 +1171,7 @@ Wallet.fromObj = function(o, readOpts) { */ Wallet.prototype.toEncryptedObj = function() { var walletObj = this.toObj(); - return this.storage.export(walletObj); + return this.storage.encrypt(walletObj); }; /** diff --git a/js/services/backupService.js b/js/services/backupService.js index 58b033d2f..874bef919 100644 --- a/js/services/backupService.js +++ b/js/services/backupService.js @@ -5,32 +5,16 @@ var BackupService = function(notification) { this.notifications = notification; }; -BackupService.prototype.getName = function(wallet) { - return (wallet.name ? (wallet.name + '-') : '') + wallet.id; -}; - BackupService.prototype.getCopayer = function(wallet) { return wallet.totalCopayers > 1 ? wallet.getMyCopayerNickname() : ''; }; -BackupService.prototype.getBackup = function(wallet) { - return wallet.toEncryptedObj(); -}; - -BackupService.prototype.getFilename = function(wallet) { - var walletName = this.getName(wallet); - var copayerName = this.getCopayer(wallet); - return (copayerName ? copayerName + '-' : '') + walletName + '-keybackup.json.aes'; -}; - -BackupService.prototype.download = function(wallet) { - var ew = this.getBackup(wallet); - var filename = this.getFilename(wallet); - - this.notifications.success('Backup created', 'Encrypted backup file saved'); +BackupService.prototype._download = function(ew, walletName, filename) { var blob = new Blob([ew], { type: 'text/plain;charset=utf-8' }); + + // show a native save dialog if we are in the shell // and pass the wallet to the shell to convert to node Buffer if (window.cshell) { @@ -49,9 +33,29 @@ BackupService.prototype.download = function(wallet) { attachments: ['base64:' + filename + '//' + btoa(ew)] }); } + this.notifications.success('Backup created', 'Encrypted backup file saved'); // otherwise lean on the browser implementation saveAs(blob, filename); }; + +BackupService.prototype.walletDownload = function(wallet) { + var ew = wallet.toEncryptedObj(); + var walletName = wallet.getName(); + var copayerName = this.getCopayer(wallet); + var filename = (copayerName ? copayerName + '-' : '') + walletName + '-keybackup.json.aes'; + this._download(ew, walletName, filename) +}; + +BackupService.prototype.profileDownload = function(iden) { + var ew = iden.toEncryptedObj(); + var name = iden.profile.getName(); + var filename = name + '-profile.json'; + this._download(ew, name, filename) +}; + + + + angular.module('copayApp.services').service('backupService', BackupService); From c9469b6da7a6537867c53d0fb73359e603292251 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 16 Oct 2014 10:20:13 -0300 Subject: [PATCH 062/182] wallet and profile backup working on the UX --- test/Identity.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Identity.js b/test/Identity.js index 45f1ec141..a63a9fedb 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -54,6 +54,7 @@ describe('Identity model', function() { profile.listWallets = sinon.stub().returns([]); profile.setLastOpenedTs = sinon.stub().yields(null);; profile.store = sinon.stub().yields(null);; + profile.getName = sinon.stub().returns('profile name');; Identity._createProfile = sinon.stub().callsArgWith(3, null, profile); From 09439d1d41c71aab5c14338e847bc4bcfc15fe80 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 16 Oct 2014 10:30:49 -0300 Subject: [PATCH 063/182] rebased to master --- js/controllers/create.js | 10 +++++++++- views/create.html | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/js/controllers/create.js b/js/controllers/create.js index 16f8fffe1..b790245ec 100644 --- a/js/controllers/create.js +++ b/js/controllers/create.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('CreateController', - function($scope, $rootScope, $location, $timeout, controllerUtils, backupService, notification) { + function($scope, $rootScope, $location, $timeout, controllerUtils, backupService, notification, defaults) { $rootScope.fromSetup = true; $scope.loading = false; @@ -30,6 +30,14 @@ angular.module('copayApp.controllers').controller('CreateController', updateRCSelect(tc); }); + $scope.$watch('networkName', function(tc) { + $scope.networkUrl = config.network[$scope.networkName].url; + }); + + $scope.showNetwork = function() { + return $scope.networkUrl != defaults.network.livenet.url && $scope.networkUrl != defaults.network.testnet.url; + }; + $scope.create = function(form) { if (form && form.$invalid) { notification.error('Error', 'Please enter the required fields'); diff --git a/views/create.html b/views/create.html index 3590c6dcf..39b534353 100644 --- a/views/create.html +++ b/views/create.html @@ -56,7 +56,7 @@

    Using network: {{networkName}} at {{networkUrl}}

    -
    +
  • From 005625c4b2defe73f4c07dc76f582f8549b303b8 Mon Sep 17 00:00:00 2001 From: bechi Date: Wed, 15 Oct 2014 16:10:40 -0300 Subject: [PATCH 064/182] fix signup & signin --- css/src/main.css | 100 +++++++++++++++++++++++++-------- css/src/mobile.css | 2 +- views/createProfile.html | 116 ++++++++++++++++++++------------------- views/home.html | 52 +++++++++--------- 4 files changed, 163 insertions(+), 107 deletions(-) diff --git a/css/src/main.css b/css/src/main.css index 8a9430261..1d3bd7b73 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -175,18 +175,33 @@ a:hover { } .home input, -.createProfile input, +.box-setup input, .open input, .join input, .setup input, .import input, .import textarea, .settings input { - background: #2C3E50 !important; - -moz-box-shadow: inset 0px 0px 3px 0px rgba(0,0,0,0.10) !important; - box-shadow: inset 0px 0px 3px 0px rgba(0,0,0,0.10) !important; - border: 0 !important; - color: #fff !important; + border-radius: 2px; + background: #EDEDED; + -moz-box-shadow: 0px 0px 0px 0px rgba(255,255,255,0.09), inset 1px 1px 0px 0px rgba(0,0,0,0.05); + box-shadow: 0px 0px 0px 0px rgba(255,255,255,0.09), inset 1px 1px 0px 0px rgba(0,0,0,0.05); + color: #2C3E42 !important; + padding: 1.2rem 0.7rem; + margin-bottom: 1.5rem; + border: 0; +} + +.box-setup ::-webkit-input-placeholder { + color: #B7C2CD; +} + +.box-setup :-ms-input-placeholder { + color: green !important; +} + +.box-setup :-moz-placeholder { + color: red !important; } .home select, @@ -248,7 +263,7 @@ a:hover { } .home, .createProfile, .settings { - margin-top: 15%; + margin-top: 8%; color: #fff; } @@ -272,7 +287,7 @@ a:hover { .logo-setup { text-align: center; - margin-top: 9%; + margin-bottom: 2rem; } .setup .logo-setup, .join .logo-setup { @@ -280,8 +295,21 @@ a:hover { } .box-setup { - padding: 20px 30px; - background: #34495E; + margin: 0 auto; + width: 310px; + padding: 1.3rem; + border-radius: 2px; + background: #FFFFFF; + -moz-box-shadow: 1px 1px 0px 0px #213140; + box-shadow: 1px 1px 0px 0px #213140; +} + +.box-setup-footer { + overflow: hidden; + margin-top: 2rem; + padding: 1rem 0 0; + border-top: 1px solid #E5E7EA; + font-size: 12px; } .box-setup label small.has-error { @@ -393,23 +421,18 @@ table.last-transactions-content { margin-bottom: 10px; } -.button-setup a { +a.button-setup { + border-radius: 3px; + border: 1px solid #B7C2CE; display: block; - padding: 20px 30px; - background: #34495E; - margin-bottom: 20px; - font-weight: 100; - font-size: 24px; + padding: 0.5rem; + background: transparent; } .button-setup a:hover { background: #3C4E60; } -.footer-setup { - overflow: hidden; -} - .dn {display: none;} .pr {position: relative;} .pa {position: absolute;} @@ -697,6 +720,10 @@ input[type=number]::-webkit-outer-spin-button { color: #1ABC9C; } +.bg-success { + background-color: #1ABC9C; +} + .label.success { background-color: #1ABC9C; } @@ -707,6 +734,10 @@ input[type=number]::-webkit-outer-spin-button { font-weight: 700; } +.bg-alert { + background-color: #C0392A; +} + .dr-notification-text { font-size: 12px; line-height: 120%; @@ -764,8 +795,8 @@ ul.pagination li.current a:hover, ul.pagination li.current a:focus { } button.radius, .button.radius { - -webkit-border-radius: 5px; - border-radius: 5px; + -webkit-border-radius: 3px; + border-radius: 3px; } /* SECONDARY */ @@ -810,7 +841,10 @@ button.primary, .button.primary { background-color: #1ABC9C; color: #fff; - border-radius: 0; + border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: 1px 1px 0px 0px #16A085; + box-shadow: 1px 1px 0px 0px #16A085; } button.primary:hover, button.primary:focus, @@ -831,6 +865,8 @@ button[disabled].primary:focus, .button.disabled.primary:focus, .button[disabled].primary:hover, .button[disabled].primary:focus { + -moz-box-shadow: 1px 1px 0px 0px #687D80; + box-shadow: 1px 1px 0px 0px #687D80; background-color: #95a5a6; color: #E6E6E6; } @@ -939,6 +975,11 @@ button.gray:focus, color: #2C3E50; } +.button, button { + color: #FFFFFF; + text-transform: uppercase; +} + .side-nav {padding: 0;} .side-nav li { @@ -1082,7 +1123,6 @@ input.ng-invalid-match, input.ng-invalid-match:focus { .text-white {color: #fff;} .text-warning {color: #CA5649;} -.footer-setup a.text-gray:hover {color: #fff;} a.text-gray:hover {color: #2C3E50;} a.text-black:hover {color: #213140;} a.text-primary:hover {color: #50E3C2;} @@ -1120,6 +1160,18 @@ a.text-warning:hover {color: #FD7262;} color: #fff; } +.box-setup h1 { + font-weight: 100; + font-size: 16px; + color: #2C3E50; + text-transform: uppercase; + text-align: center; +} + +.box-setup form { + margin: 0; +} + .joyride-tip-guide { width: 150px; background: #213140; diff --git a/css/src/mobile.css b/css/src/mobile.css index b36b42fbe..2e639bf2a 100644 --- a/css/src/mobile.css +++ b/css/src/mobile.css @@ -180,7 +180,7 @@ } .box-setup { - margin-bottom: 50px; + width: 100%; } .footer-setup { diff --git a/views/createProfile.html b/views/createProfile.html index 3da07d81a..c3116642b 100644 --- a/views/createProfile.html +++ b/views/createProfile.html @@ -1,70 +1,74 @@
    -
    -
    - -
    -
    - Copay now needs a profile to access wallets.
    - You can import current your wallets after creating your profile. -
    -
    - -
    Retreiving information from storage...
    -
    -
    + +
    +
    Copay
    -
    -
    -

    Create Profile

    -
    -
    -
    - - - -
    +
    +
    + +
    +
    + Copay now needs a profile to access wallets. + You can import your current wallets after creating your frofile +
    +
    +
    +

    Create Profile

    + +
    +
    + + +
    - +
    + -
    - - - -

    + + - - {{'Passwords must match'|translate}} -

    -
    - - -
    - « Back - -
    - + !profileForm.repeatpassword.$invalid">
    + +

    + + {{'Passwords must match'|translate}} +

    +
    +
    + +
    + +
    diff --git a/views/home.html b/views/home.html index 80400d903..eb8bb7812 100644 --- a/views/home.html +++ b/views/home.html @@ -4,35 +4,35 @@ Retreiving information from storage...
    -
    -
    +
    +
    Copay
    -
    -
    -

    Login

    -
    - - - -
    - -
    -
    -
    - - - From 22c378b080386f0a2704d2c98757e40394605301 Mon Sep 17 00:00:00 2001 From: bechi Date: Thu, 16 Oct 2014 10:45:06 -0300 Subject: [PATCH 065/182] side bar details --- css/src/main.css | 3 ++- views/includes/sidebar.html | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/css/src/main.css b/css/src/main.css index 1d3bd7b73..0e3574011 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -109,7 +109,8 @@ body, html{ } header { - padding: 15px 20px 5px; + padding: 1rem; + background-color: #1ABC9C; } diff --git a/views/includes/sidebar.html b/views/includes/sidebar.html index 83e20b5ec..fccee319d 100644 --- a/views/includes/sidebar.html +++ b/views/includes/sidebar.html @@ -1,12 +1,12 @@ - - - +
    +
    +
    +
    From 43c0eb9cb6c6a74ebbca22cb16ef8ea2963394e5 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Wed, 15 Oct 2014 17:18:30 -0300 Subject: [PATCH 066/182] handle 0 wallets --- index.html | 2 +- js/controllers/home.js | 8 ++++---- js/models/Identity.js | 3 +-- js/services/controllerUtils.js | 7 +++++-- views/includes/sidebar.html | 26 ++++++++++++-------------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/index.html b/index.html index c49932b8d..ba0becd4a 100644 --- a/index.html +++ b/index.html @@ -65,7 +65,7 @@ class="sidebar" ng-if="$root.iden">
    -
    +
    diff --git a/js/controllers/home.js b/js/controllers/home.js index 0b606146e..fc11bd7d9 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -3,15 +3,15 @@ angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) { controllerUtils.redirIfLogged(); - $scope.retreiving =true; + $scope.retreiving = true; copay.Identity.anyProfile({ pluginManager: pluginManager, }, function(any) { - $scope.retreiving =false; + $scope.retreiving = false; if (!any) $location.path('/createProfile'); }); - + $scope.openProfile = function(form) { if (form && form.$invalid) { @@ -29,7 +29,7 @@ angular.module('copayApp.controllers').controller('HomeController', function($sc if (err && !iden) { console.log('Error:' + err) controllerUtils.onErrorDigest( - $scope, (err.toString()||'').match('PNOTFOUND') ? 'Profile not found' : 'Unknown error'); + $scope, (err.toString() || '').match('PNOTFOUND') ? 'Profile not found' : 'Unknown error'); } else { controllerUtils.bindProfile($scope, iden, firstWallet); } diff --git a/js/models/Identity.js b/js/models/Identity.js index 2a0472ab4..15e7953b7 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -190,9 +190,8 @@ Identity.open = function(email, password, opts, cb) { iden.profile = profile; var wids = _.pluck(iden.listWallets(), 'id'); - if (!wids || !wids.length) - return new Error('Could not open any wallet from profile'); + return cb(new Error('Could not open any wallet from profile'), iden); // Open All wallets from profile //This could be optional, or opts.onlyOpen = wid diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index 6f173e71a..668fe2539 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -217,10 +217,13 @@ angular.module('copayApp.services') }; root.bindProfile = function($scope, iden, w) { - root.setupGlobalVariables(iden); root.rebindWallets($scope, iden); - root.setFocusedWallet(w); + if (w) { + root.setFocusedWallet(w); + } else { + $location.path('/manage'); + } }; diff --git a/views/includes/sidebar.html b/views/includes/sidebar.html index fccee319d..f1bc8b851 100644 --- a/views/includes/sidebar.html +++ b/views/includes/sidebar.html @@ -10,7 +10,6 @@
      -
    - +
    - From d461ddd600bb06661af49917d202b7731f40debf Mon Sep 17 00:00:00 2001 From: Matias Pando Date: Thu, 16 Oct 2014 11:25:45 -0300 Subject: [PATCH 067/182] Fixes access to settings and a small typo --- js/routes.js | 180 +++++++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/js/routes.js b/js/routes.js index 4ab9abed8..49473ee04 100644 --- a/js/routes.js +++ b/js/routes.js @@ -2,100 +2,100 @@ //Setting up route angular -.module('copayApp') -.config(function($routeProvider) { + .module('copayApp') + .config(function($routeProvider) { - $routeProvider - .when('/', { - templateUrl: 'views/home.html', - }) - .when('/createProfile', { - templateUrl: 'views/createProfile.html', - }) - .when('/unsupported', { - templateUrl: 'views/unsupported.html' - }) - .when('/uri-payment/:data', { - templateUrl: 'views/uri-payment.html' - }) - .when('/join', { - templateUrl: 'views/join.html', - logged: true - }) - .when('/import', { - templateUrl: 'views/import.html', - logged: true - }) - .when('/create', { - templateUrl: 'views/create.html', - logged: true - }) - .when('/copayers', { - templateUrl: 'views/copayers.html', - logged: true - }) - .when('/receive', { - templateUrl: 'views/addresses.html', - logged: true - }) - .when('/history', { - templateUrl: 'views/transactions.html', - logged: true - }) - .when('/send', { - templateUrl: 'views/send.html', - logged: true - }) - .when('/more', { - templateUrl: 'views/more.html', - logged: true - }) - .when('/settings', { - templateUrl: 'views/settings.html', - logged: true - }) - .when('/warning', { - templateUrl: 'views/warning.html', - logged: true - }) - .when('/manage', { - templateUrl: 'views/manage.html', - logged: true - }) - .otherwise({ - templateUrl: 'views/errors/404.html', - title: 'Error' + $routeProvider + .when('/', { + templateUrl: 'views/home.html', + }) + .when('/createProfile', { + templateUrl: 'views/createProfile.html', + }) + .when('/unsupported', { + templateUrl: 'views/unsupported.html' + }) + .when('/uri-payment/:data', { + templateUrl: 'views/uri-payment.html' + }) + .when('/join', { + templateUrl: 'views/join.html', + logged: true + }) + .when('/import', { + templateUrl: 'views/import.html', + logged: true + }) + .when('/create', { + templateUrl: 'views/create.html', + logged: true + }) + .when('/copayers', { + templateUrl: 'views/copayers.html', + logged: true + }) + .when('/receive', { + templateUrl: 'views/addresses.html', + logged: true + }) + .when('/history', { + templateUrl: 'views/transactions.html', + logged: true + }) + .when('/send', { + templateUrl: 'views/send.html', + logged: true + }) + .when('/more', { + templateUrl: 'views/more.html', + logged: true + }) + .when('/settings', { + templateUrl: 'views/settings.html', + logged: false + }) + .when('/warning', { + templateUrl: 'views/warning.html', + logged: true + }) + .when('/manage', { + templateUrl: 'views/manage.html', + logged: true + }) + .otherwise({ + templateUrl: 'views/errors/404.html', + title: 'Error' + }); }); -}); //Setting HTML5 Location Mode angular -.module('copayApp') -.config(function($locationProvider, $idleProvider, $keepaliveProvider) { - $locationProvider - .html5Mode(false) - .hashPrefix('!'); - // IDLE timeout - var timeout = config.wallet.idleDurationMin * 60 || 300; - $idleProvider.idleDuration(timeout); // in seconds - $idleProvider.warningDuration(40); // in seconds - $keepaliveProvider.interval(30); // in seconds -}) -.run(function($rootScope, $location, $idle, gettextCatalog) { - gettextCatalog.currentLanguage = config.defaultLanguage; - $idle.watch(); - $rootScope.$on('$routeChangeStart', function(event, next, current) { - if (!localStorage || localStorage.length < 1) { - $location.path('unsupported'); - } else { - if (!$rootScope.iden && next.logged) { - console.log('not logged... redirecting') - $idle.unwatch(); - $location.path('/'); + .module('copayApp') + .config(function($locationProvider, $idleProvider, $keepaliveProvider) { + $locationProvider + .html5Mode(false) + .hashPrefix('!'); + // IDLE timeout + var timeout = config.wallet.idleDurationMin * 60 || 300; + $idleProvider.idleDuration(timeout); // in seconds + $idleProvider.warningDuration(40); // in seconds + $keepaliveProvider.interval(30); // in seconds + }) + .run(function($rootScope, $location, $idle, gettextCatalog) { + gettextCatalog.currentLanguage = config.defaultLanguage; + $idle.watch(); + $rootScope.$on('$routeChangeStart', function(event, next, current) { + if (!localStorage || localStorage.length < 1) { + $location.path('unsupported'); + } else { + if (!$rootScope.iden && next.logged) { + console.log('not logged... redirecting') + $idle.unwatch(); + $location.path('/'); + } } - } + }); + }) + .config(function($compileProvider) { + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|tel|chrome-extension|resource):/); }); -}) -.config(function($compileProvider) { - $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|tel|chrome-extension|resource):/); -}); From 046546e8d358e1f4cd9312ab209a97e1203b73a9 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 16 Oct 2014 15:06:39 -0300 Subject: [PATCH 068/182] cached wallet balance --- js/services/controllerUtils.js | 51 +++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index 668fe2539..b58cd37d1 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -239,22 +239,19 @@ angular.module('copayApp.services') } }; + var _balanceCache = {}; root.updateBalance = function(w, cb) { - w = w || $rootScope.wallet; - if (!w) return root.onErrorDigest(); - if (!w.isReady()) return; - console.log('## Updating balance of:' + w.id) - - $rootScope.balanceByAddr = {}; - $rootScope.updatingBalance = true; - - w.getBalance(function(err, balanceSat, balanceByAddrSat, safeBalanceSat) { - if (err) throw err; - + var updateScope = function(w, data, cb2) { var satToUnit = 1 / w.settings.unitToSatoshi; var COIN = bitcore.util.COIN; + var balanceSat = data.balanceSat; + var balanceByAddrSat = data.balanceByAddrSat; + var safeBalanceSat = data.safeBalanceSat; + if (root.isFocusedWallet(w.getId())) { + $rootScope.balanceByAddr = {}; + $rootScope.totalBalance = balanceSat * satToUnit; $rootScope.totalBalanceBTC = (balanceSat / COIN); $rootScope.availableBalance = safeBalanceSat * satToUnit; @@ -269,19 +266,47 @@ angular.module('copayApp.services') } $rootScope.balanceByAddr = balanceByAddr; root.updateAddressList(); - $rootScope.updatingBalance = false; rateService.whenAvailable(function() { $rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, w.settings.alternativeIsoCode); $rootScope.alternativeIsoCode = w.settings.alternativeIsoCode; $rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, w.settings.alternativeIsoCode); $rootScope.alternativeConversionRate = rateService.toFiat(100000000, w.settings.alternativeIsoCode); - return cb ? cb() : null; + return cb2 ? cb2() : null; }); } else { // TODO console.log('TODO: balance updated of a unfocused wallet'); } + }; + + w = w || $rootScope.wallet; + if (!w) return root.onErrorDigest(); + if (!w.isReady()) return; + console.log('## Updating balance of:' + w.id) + + + var wid = w.getId(); + + if (_balanceCache[wid]) { + updateScope(w, _balanceCache[wid]); + } else { + $rootScope.updatingBalance = true; + } + + w.getBalance(function(err, balanceSat, balanceByAddrSat, safeBalanceSat) { + if (err) throw err; + + _balanceCache[wid] = { + balanceSat: balanceSat, + balanceByAddrSat: balanceByAddrSat, + safeBalanceSat: safeBalanceSat, + }; + + updateScope(w, _balanceCache[wid], function() { + $rootScope.updatingBalance = false; + cb(); + }); }); }; From 2beda0c3548cb44fef0443d054ee0fc3b74eafdc Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 16 Oct 2014 16:02:32 -0300 Subject: [PATCH 069/182] refreshing wallet clears balance cache --- js/controllers/sidebar.js | 1 + js/services/controllerUtils.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/js/controllers/sidebar.js b/js/controllers/sidebar.js index 853353905..a634fc641 100644 --- a/js/controllers/sidebar.js +++ b/js/controllers/sidebar.js @@ -39,6 +39,7 @@ angular.module('copayApp.controllers').controller('SidebarController', function( if (w.isReady()) { w.sendWalletReady(); if ($rootScope.addrInfos.length > 0) { + controllerUtils.clearBalanceCache(w); controllerUtils.updateBalance(w, function() { $rootScope.$digest(); }); diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index b58cd37d1..067bbffd3 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -240,6 +240,10 @@ angular.module('copayApp.services') }; var _balanceCache = {}; + root.clearBalanceCache = function(w) { + delete _balanceCache[w.getId()]; + }; + root.updateBalance = function(w, cb) { var updateScope = function(w, data, cb2) { var satToUnit = 1 / w.settings.unitToSatoshi; From 8b9ad143bbd16ecdf05b71c6bf4394865988a1e4 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 16 Oct 2014 17:36:17 -0300 Subject: [PATCH 070/182] ignore if copaybundle and copaymain were modified when grunt is watching --- Gruntfile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gruntfile.js b/Gruntfile.js index b1b121723..41b8a3c8b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -57,6 +57,8 @@ module.exports = function(grunt) { 'js/models/*.js', 'plugins/*.js', 'js/*.js', + '!js/copayBundle.js', + '!js/copayMain.js' ], tasks: ['shell:dev'] }, From 0e1cdda52d7c886d5583cb94adbbbea6d1ae9db4 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 16 Oct 2014 17:39:22 -0300 Subject: [PATCH 071/182] import a wallet inside a profile --- css/src/main.css | 15 -------------- js/controllers/import.js | 42 +++++++++++++++------------------------ js/models/Identity.js | 24 +++++++++++++++------- js/models/Storage.js | 1 + js/services/passphrase.js | 2 +- views/import.html | 12 +++-------- 6 files changed, 38 insertions(+), 58 deletions(-) diff --git a/css/src/main.css b/css/src/main.css index 0e3574011..97d1e9dc4 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -180,8 +180,6 @@ a:hover { .open input, .join input, .setup input, -.import input, -.import textarea, .settings input { border-radius: 2px; background: #EDEDED; @@ -268,19 +266,6 @@ a:hover { color: #fff; } -.import fieldset, .settings fieldset { - border: 1px solid #3C5269; -} - -.import fieldset legend, .settings fieldset legend { - background: transparent; - color: #fff; - font-weight: normal; -} - -.import input[type="file"], -.import label, -.import label small, .settings label, .settings label small { color: #fff; diff --git a/js/controllers/import.js b/js/controllers/import.js index 49c4d9c58..717935593 100644 --- a/js/controllers/import.js +++ b/js/controllers/import.js @@ -1,8 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('ImportController', - function($scope, $rootScope, $location, identity, controllerUtils, Passphrase, notification, isMobile) { - controllerUtils.redirIfLogged(); + function($scope, $rootScope, $location, controllerUtils, Passphrase, notification, isMobile) { $scope.title = 'Import a backup'; $scope.importStatus = 'Importing wallet - Reading backup...'; @@ -17,38 +16,30 @@ angular.module('copayApp.controllers').controller('ImportController', } var _importBackup = function(encryptedObj) { - Passphrase.getBase64Async($scope.password, function(passphrase) { - updateStatus('Importing wallet - Setting things up...'); - var w, errMsg; + var password = $scope.password; + updateStatus('Importing wallet - Setting things up...'); + var skipFields = []; + if ($scope.skipPublicKeyRing) + skipFields.push('publicKeyRing'); - var skipFields = []; - if ($scope.skipPublicKeyRing) - skipFields.push('publicKeyRing'); - - if ($scope.skipTxProposals) - skipFields.push('txProposals'); - - // try to import encrypted wallet with passphrase - try { - w = identity.fromEncryptedObj(encryptedObj, passphrase, skipFields); - } catch (e) { - errMsg = e.message; - } + if ($scope.skipTxProposals) + skipFields.push('txProposals'); + $rootScope.iden.importWallet(encryptedObj, password, skipFields, function(err, w) { if (!w) { $scope.loading = false; - notification.error('Error', errMsg || 'Wrong password'); + notification.error('Error', err || 'Wrong password'); $rootScope.$digest(); return; } // if wallet was never used, we're done if (!w.isReady()) { - $rootScope.wallet = w; - controllerUtils.startNetwork($rootScope.wallet, $scope); + controllerUtils.installWalletHandlers($scope, w); + controllerUtils.setFocusedWallet(w); return; } - + // if it was used, we need to scan for indices w.updateIndexes(function(err) { updateStatus('Importing wallet - We are almost there...'); @@ -56,11 +47,10 @@ angular.module('copayApp.controllers').controller('ImportController', $scope.loading = false; notification.error('Error', 'Error updating indexes: ' + err); } - $rootScope.wallet = w; - controllerUtils.startNetwork($rootScope.wallet, $scope); + controllerUtils.installWalletHandlers($scope, w); + controllerUtils.setFocusedWallet(w); }); - - }); + }); }; $scope.openFileDialog = function() { diff --git a/js/models/Identity.js b/js/models/Identity.js index 15e7953b7..73e27818a 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -60,8 +60,8 @@ Identity._newWallet = function(opts) { return new Wallet(opts); }; -Identity._walletFromObj = function(o, s, n, b, skip) { - return Wallet.fromObj(o, s, n, b, skip); +Identity._walletFromObj = function(o, readOpts) { + return Wallet.fromObj(o, readOpts); }; Identity._walletRead = function(id, r, cb) { @@ -299,6 +299,7 @@ Identity.prototype.close = function(cb) { * @return {Wallet} */ Identity.prototype.importWallet = function(base64, password, skipFields, cb) { + var self = this; preconditions.checkArgument(password); preconditions.checkArgument(cb); @@ -307,13 +308,22 @@ Identity.prototype.importWallet = function(base64, password, skipFields, cb) { var obj = this.storage.decrypt(base64); this.storage.restorePassphrase(); - if (!obj) return false; - var w = Identity._walletFromObj(obj, this.storage, this.networkOpts, this.blockchainOpts); - console.log('[Identity.js.307:Identity:]', w); //TODO + var readOpts = { + storage: this.storage, + networkOpts: this.networkOpts, + blockchainOpts: this.blockchainOpts, + skipFields: skipFields + }; + + if (!obj) return cb(null); + var w = Identity._walletFromObj(obj, readOpts); this._checkVersion(w.version); this.addWallet(w, function(err) { - if (err) return cb(err); - w.store(cb); + if (err) return cb(err, null); + self.openWallets.push(w); + self.store(null, function(err) { + return cb(err, w); + }); }); }; diff --git a/js/models/Storage.js b/js/models/Storage.js index 7714bb2fa..e0f771e85 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -98,6 +98,7 @@ Storage.prototype._decrypt = function(base64) { decryptedStr = decrypted.toString(CryptoJS.enc.Utf8); } catch (e) { // Error while decrypting + log.debug(e.message); return null; } return decryptedStr; diff --git a/js/services/passphrase.js b/js/services/passphrase.js index 00ff9e2be..3b8f3d86c 100644 --- a/js/services/passphrase.js +++ b/js/services/passphrase.js @@ -1,4 +1,4 @@ 'use strict'; angular.module('copayApp.services') - .value('Passphrase', new copay.Passphrase(config.passphrase)); + .value('Passphrase', new copay.Passphrase(config.passphraseConfig)); diff --git a/views/import.html b/views/import.html index 839950501..2ba4255e2 100644 --- a/views/import.html +++ b/views/import.html @@ -4,15 +4,10 @@ {{ importStatus|translate }}
    -
    -
    - Copay -
    -
    -
    +
    +
    -
    -

    {{title|translate}}

    +

    {{title|translate}}

    @@ -72,7 +67,6 @@
    -
    From ee7993dccb3311cfb2329155b6532c0c17b1aaa1 Mon Sep 17 00:00:00 2001 From: bechi Date: Thu, 16 Oct 2014 12:01:11 -0300 Subject: [PATCH 072/182] side bar refactory --- views/includes/sidebar.html | 111 +++++++++++++++++------------------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/views/includes/sidebar.html b/views/includes/sidebar.html index f1bc8b851..a715102c3 100644 --- a/views/includes/sidebar.html +++ b/views/includes/sidebar.html @@ -1,72 +1,63 @@
    -
    - - - +
    +
    +
    + + + +
    + [TESTNET]
    -
    + From 62502bc69814b75351ae06567640323dd7b3afa9 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 16 Oct 2014 12:48:47 -0300 Subject: [PATCH 073/182] implemented wallet selection in sidebar --- js/controllers/sidebar.js | 4 ++++ views/includes/sidebar.html | 47 ++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/js/controllers/sidebar.js b/js/controllers/sidebar.js index a634fc641..965f5c10b 100644 --- a/js/controllers/sidebar.js +++ b/js/controllers/sidebar.js @@ -78,6 +78,10 @@ angular.module('copayApp.controllers').controller('SidebarController', function( } $scope.switchWallet = function(wid) { + $scope.walletSelection = false; controllerUtils.setFocusedWallet(wid); }; + $scope.toggleWalletSelection = function() { + $scope.walletSelection = !$scope.walletSelection; + }; }); diff --git a/views/includes/sidebar.html b/views/includes/sidebar.html index a715102c3..1cc55db1c 100644 --- a/views/includes/sidebar.html +++ b/views/includes/sidebar.html @@ -1,25 +1,22 @@
    -
    -