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',