From cd3f2b8b90f12bc9661d3f74650cc990cbc37ce6 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 23 Sep 2014 14:54:09 -0300 Subject: [PATCH] Added methods for storing/reading from sinlge key-value pair --- js/models/Storage.js | 68 +++++++++++++++++++- js/models/WalletFactory.js | 48 ++++++++++++-- test/test.Storage.js | 126 ++++++++++++++++++++++++++++++++++++- test/test.WalletFactory.js | 1 - 4 files changed, 231 insertions(+), 12 deletions(-) diff --git a/js/models/Storage.js b/js/models/Storage.js index 39f6bd07f..9b5fb22f1 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -94,7 +94,6 @@ Storage.prototype.getGlobal = function(k, cb) { // 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); }; @@ -147,6 +146,7 @@ Storage.prototype.readWallet = function(walletId, cb) { var keys = _.filter(allKeys, function(k) { if (k.indexOf(walletId + '::') === 0) return true; }); + if (keys.length === 0) return cb(null); var count = keys.length; _.each(keys, function(k) { self._read(k, function(v) { @@ -157,6 +157,19 @@ Storage.prototype.readWallet = function(walletId, cb) { }); }; +Storage.prototype.readWallet2 = 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 !== 1) throw new Error('WALLETNOTFOUND'); + self._read(keys[0], function(v) { + return cb(v); + }) + }); +}; + Storage.prototype.getMany = function(walletId, keys, cb) { preconditions.checkArgument(cb); @@ -256,6 +269,28 @@ Storage.prototype.getWallets = function(cb) { }); }; +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.deleteWallet = function(walletId, cb) { preconditions.checkArgument(walletId); preconditions.checkArgument(cb); @@ -288,17 +323,32 @@ Storage.prototype.deleteWallet = function(walletId, cb) { } }); +}; +Storage.prototype.deleteWallet2 = 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); -} +}; //obj contains keys to be set Storage.prototype.setFromObj = function(walletId, obj, cb) { @@ -319,6 +369,17 @@ Storage.prototype.setFromObj = function(walletId, obj, cb) { } }; +Storage.prototype.setFromObj2 = function(walletId, obj, cb) { + preconditions.checkArgument(cb); + var self = this; + + var key = 'wallet::' + walletId + ((obj.opts && obj.opts.name) ? '_' + obj.opts.name : ''); + self._write(key, obj, function() { + return cb(); + }); +}; + + // remove all values Storage.prototype.clearAll = function(cb) { this.storage.clear(cb); @@ -334,4 +395,5 @@ Storage.prototype.export = function(obj) { return this._encrypt(string); }; + module.exports = Storage; diff --git a/js/models/WalletFactory.js b/js/models/WalletFactory.js index 55216f968..b2b62e35b 100644 --- a/js/models/WalletFactory.js +++ b/js/models/WalletFactory.js @@ -142,6 +142,39 @@ WalletFactory.prototype.import = function(base64, passphrase, skipFields) { return w; }; +WalletFactory.prototype.migrateWallet = function(walletId, passphrase, cb) { + var self = this; + + self.storage.setPassphrase(passphrase); + self.storage.setFromObj = self.storage.setFromObj2; + + var reconfigureStorage = function() { + self.storage.getWallets = self.storage.getWallets2; + self.storage.readWallet = self.storage.readWallet2; + self.storage.deleteWallet = self.storage.deleteWallet2; + self.storage.get = function() { + throw 'DO NOT USE' + }; + self.storage.set = function() { + throw 'DO NOT USE' + }; + }; + + self.read(walletId, null, function(err, wallet) { + if (err) return cb(err); + + wallet.store(); + self.storage.deleteWallet(walletId, function() { + self.storage.removeGlobal('nameFor::' + walletId, function() { + reconfigureStorage(); + return cb(); + }); + }); + }); + +}; + + /** * @desc Retrieve a wallet from storage * @param {string} walletId - the wallet id @@ -153,7 +186,9 @@ WalletFactory.prototype.read = function(walletId, skipFields, cb) { err; var obj = {}; + console.log('aƔaa'); this.storage.readWallet(walletId, function(ret) { + console.log('bbb'); _.each(Wallet.PERSISTED_PROPERTIES, function(p) { obj[p] = ret[p]; }); @@ -301,12 +336,15 @@ WalletFactory.prototype.open = function(walletId, passphrase, cb) { preconditions.checkArgument(cb); var self = this; self.storage.setPassphrase(passphrase); - self.read(walletId, null, function(err, w) { - if (err) return cb(err); - w.store(function(err) { - self.storage.setLastOpened(walletId, function() { - return cb(err, w); + self.migrateWallet(walletId, passphrase, function() { + self.read(walletId, null, function(err, w) { + if (err) return cb(err); + + w.store(function(err) { + self.storage.setLastOpened(walletId, function() { + return cb(err, w); + }); }); }); }); diff --git a/test/test.Storage.js b/test/test.Storage.js index 03f5e7916..be1dbafce 100644 --- a/test/test.Storage.js +++ b/test/test.Storage.js @@ -200,6 +200,35 @@ describe('Storage model', function() { }); }); + describe('#getWallets2', function() { + it('should retreive wallets from storage', function(done) { + var w1 = { + name: 'juan', + opts: { + name: 'wallet1' + } + }; + var w2 = { + name: 'pepe' + }; + s.setFromObj2('1', w1, function() { + s.setFromObj2('2', w2, function() { + s.getWallets2(function(ws) { + ws[0].should.deep.equal({ + id: '1', + name: 'wallet1', + }); + ws[1].should.deep.equal({ + id: '2', + name: undefined + }); + done(); + }); + }); + }); + }); + }); + describe('#deleteWallet', function() { it('should fail to delete a unexisting wallet', function(done) { s.set('1', "hola", 'juan', function() { @@ -231,6 +260,54 @@ describe('Storage model', function() { }); }); + describe('#deleteWallet2', function() { + it('should fail to delete a unexisting wallet', function(done) { + var w1 = { + name: 'juan', + opts: { + name: 'wallet1' + } + }; + var w2 = { + name: 'pepe' + }; + + s.setFromObj2('1', w1, function() { + s.setFromObj2('2', w2, function() { + s.deleteWallet2('3', function(err) { + err.toString().should.include('WNOTFOUND'); + done(); + }); + }); + }); + }); + + it('should delete a wallet', function(done) { + var w1 = { + name: 'juan', + opts: { + name: 'wallet1' + } + }; + var w2 = { + name: 'pepe' + }; + + s.setFromObj2('1', w1, function() { + s.setFromObj2('2', w2, function() { + s.deleteWallet2('1', function(err) { + should.not.exist(err); + s.getWallets2(function(ws) { + ws.length.should.equal(1); + ws[0].id.should.equal('2'); + done(); + }); + }); + }); + }); + }); + }); + describe('#readWallet', function() { it('should read wallet', function(done) { var data = { @@ -255,6 +332,34 @@ describe('Storage model', function() { }); }); + describe('#readWallet2', function() { + it('should read wallet', function(done) { + var data = { + 'wallet::id1_wallet1': { + a: 'x', + b: 'y' + }, + 'wallet::id2': { + c: 'z' + }, + }; + s.storage.allKeys = sinon.stub().yields(_.keys(data)); + sinon.stub(s, '_read', function(k, cb) { + return cb(data[k]); + }); + s.readWallet2('id1', function(w) { + w.should.exist; + w.hasOwnProperty('a').should.be.true; + w.hasOwnProperty('b').should.be.true; + w.hasOwnProperty('c').should.be.false; + w.a.should.equal('x'); + w.b.should.equal('y'); + s._read.restore(); + done(); + }); + }); + }); + describe('#setFromObj', function() { it('set localstorage from an object', function(done) { s.setFromObj('id1', { @@ -271,6 +376,24 @@ describe('Storage model', function() { }); }); + describe('#setFromObj2', function() { + it('should store from an object as single key', function(done) { + s.setFromObj2('id1', { + 'key': 'val', + 'opts': { + 'name': 'nameid1' + }, + }, function() { + s._read('wallet::id1_nameid1', function(r) { + r.should.exist; + r.key.should.exist; + r.key.should.equal('val'); + r.opts.name.should.equal('nameid1'); + done(); + }); + }); + }); + }); describe('#globals', function() { it('should set, get and remove keys', function(done) { @@ -335,11 +458,8 @@ describe('Storage model', function() { wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6'); wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm'); wo.privateKey.networkName.should.equal('testnet'); - - }); }); - }); var legacyPassword1 = '1DUpLRbuVpgLkcEY8gY8iod/SmA7+OheGZJ9PtvmTlvNE0FkEWpCKW9STdzXYJqbn0wiAapE4ojHNYj2hjYYAQ=='; diff --git a/test/test.WalletFactory.js b/test/test.WalletFactory.js index 63a392ac0..71f8bac10 100644 --- a/test/test.WalletFactory.js +++ b/test/test.WalletFactory.js @@ -549,7 +549,6 @@ describe('WalletFactory model', function() { }); }); - });