Merge pull request #1307 from matiu/feature/drive
Feature/ Async storage + Google Drive example
This commit is contained in:
commit
3a79f039cd
38 changed files with 1980 additions and 1192 deletions
14
js/app.js
14
js/app.js
|
|
@ -15,7 +15,11 @@ if (localConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
var copayApp = window.copayApp = angular.module('copayApp', [
|
||||
var log = function() {
|
||||
if (config.verbose) console.log(arguments);
|
||||
}
|
||||
|
||||
var modules = [
|
||||
'ngRoute',
|
||||
'angularMoment',
|
||||
'mm.foundation',
|
||||
|
|
@ -26,7 +30,13 @@ var copayApp = window.copayApp = angular.module('copayApp', [
|
|||
'copayApp.services',
|
||||
'copayApp.controllers',
|
||||
'copayApp.directives',
|
||||
]);
|
||||
];
|
||||
|
||||
if (Object.keys(config.plugins).length)
|
||||
modules.push('angularLoad');
|
||||
|
||||
|
||||
var copayApp = window.copayApp = angular.module('copayApp', modules);
|
||||
|
||||
copayApp.config(function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@ angular.module('copayApp.controllers').controller('CreateController',
|
|||
privateKeyHex: $scope.private,
|
||||
networkName: $scope.networkName,
|
||||
};
|
||||
var w = walletFactory.create(opts);
|
||||
controllerUtils.startNetwork(w, $scope);
|
||||
walletFactory.create(opts, function(err, w) {
|
||||
controllerUtils.startNetwork(w, $scope);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('HomeController',
|
||||
function($scope, $rootScope, $location, walletFactory, notification, controllerUtils) {
|
||||
|
||||
controllerUtils.redirIfLogged();
|
||||
angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, walletFactory, notification, controllerUtils) {
|
||||
|
||||
$scope.loading = false;
|
||||
$scope.hasWallets = (walletFactory.getWallets() && walletFactory.getWallets().length > 0) ? true : false;
|
||||
controllerUtils.redirIfLogged();
|
||||
|
||||
$scope.retreiving = true;
|
||||
walletFactory.getWallets(function(err,ret) {
|
||||
$scope.retreiving = false;
|
||||
$scope.hasWallets = (ret && ret.length > 0) ? true : false;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -119,7 +119,12 @@ angular.module('copayApp.controllers').controller('JoinController',
|
|||
$scope.loading = true;
|
||||
|
||||
Passphrase.getBase64Async($scope.joinPassword, function(passphrase) {
|
||||
walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.private, function(err, w) {
|
||||
walletFactory.joinCreateSession({
|
||||
secret: $scope.connectionId,
|
||||
nickname: $scope.nickname,
|
||||
passphrase: passphrase,
|
||||
privateHex: $scope.private,
|
||||
}, function(err, w) {
|
||||
$scope.loading = false;
|
||||
if (err || !w) {
|
||||
if (err === 'joinError')
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ angular.module('copayApp.controllers').controller('MoreController',
|
|||
value: 100000000,
|
||||
decimals: 8
|
||||
}];
|
||||
|
||||
$scope.selectedAlternative = {
|
||||
name: w.settings.alternativeName,
|
||||
isoCode: w.settings.alternativeIsoCode
|
||||
|
|
|
|||
|
|
@ -14,15 +14,32 @@ angular.module('copayApp.controllers').controller('OpenController', function($sc
|
|||
};
|
||||
$rootScope.fromSetup = false;
|
||||
$scope.loading = false;
|
||||
$scope.wallets = walletFactory.getWallets().sort(cmp);
|
||||
$scope.selectedWalletId = walletFactory.storage.getLastOpened() || ($scope.wallets[0] && $scope.wallets[0].id);
|
||||
$scope.retreiving = true;
|
||||
|
||||
walletFactory.getWallets(function(err, wallets) {
|
||||
|
||||
if (err || !wallets || !wallets.length) {
|
||||
$location.path('/');
|
||||
} else {
|
||||
$scope.retreiving = false;
|
||||
$scope.wallets = wallets.sort(cmp);
|
||||
|
||||
walletFactory.storage.getLastOpened(function(ret) {
|
||||
if (ret && _.indexOf(_.pluck($scope.wallets, 'id')) == -1)
|
||||
ret = null;
|
||||
|
||||
$scope.selectedWalletId = ret || ($scope.wallets[0] && $scope.wallets[0].id);
|
||||
|
||||
setTimeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.openPassword = '';
|
||||
$scope.isMobile = !!window.cordova;
|
||||
|
||||
if (!$scope.wallets.length){
|
||||
$location.path('/');
|
||||
}
|
||||
|
||||
$scope.open = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
notification.error('Error', 'Please enter the required fields');
|
||||
|
|
@ -34,19 +51,16 @@ angular.module('copayApp.controllers').controller('OpenController', function($sc
|
|||
|
||||
Passphrase.getBase64Async(password, function(passphrase) {
|
||||
var w, errMsg;
|
||||
try {
|
||||
w = walletFactory.open($scope.selectedWalletId, passphrase);
|
||||
} catch (e) {
|
||||
errMsg = e.message;
|
||||
};
|
||||
if (!w) {
|
||||
$scope.loading = false;
|
||||
notification.error('Error', errMsg || 'Wrong password');
|
||||
$rootScope.$digest();
|
||||
return;
|
||||
}
|
||||
$rootScope.updatingBalance = true;
|
||||
controllerUtils.startNetwork(w, $scope);
|
||||
walletFactory.open($scope.selectedWalletId, passphrase, function(err, w) {
|
||||
if (!w) {
|
||||
$scope.loading = false;
|
||||
notification.error('Error', err.errMsg || 'Wrong password');
|
||||
$rootScope.$digest();
|
||||
} else {
|
||||
$rootScope.updatingBalance = true;
|
||||
controllerUtils.startNetwork(w, $scope);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
320
js/models/Storage.js
Normal file
320
js/models/Storage.js
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
'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._key = function(walletId, k) {
|
||||
return walletId + '::' + k;
|
||||
};
|
||||
// get value by key
|
||||
Storage.prototype.get = function(walletId, k, cb) {
|
||||
preconditions.checkArgument(walletId, k, cb);
|
||||
this._read(this._key(walletId, k), 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.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// set value for key
|
||||
Storage.prototype.set = function(walletId, k, v, cb) {
|
||||
preconditions.checkArgument(walletId && k && cb);
|
||||
|
||||
if (_.isUndefined(v)) return cb();
|
||||
|
||||
this._write(this._key(walletId, k), v, cb);
|
||||
};
|
||||
|
||||
// remove value for key
|
||||
Storage.prototype.remove = function(walletId, k, cb) {
|
||||
preconditions.checkArgument(walletId && k && cb);
|
||||
this.removeGlobal(this._key(walletId, k), cb);
|
||||
};
|
||||
|
||||
Storage.prototype.setName = function(walletId, name, cb) {
|
||||
preconditions.checkArgument(walletId && name && cb);
|
||||
this.setGlobal('nameFor::' + walletId, name, cb);
|
||||
};
|
||||
|
||||
Storage.prototype.getName = function(walletId, cb) {
|
||||
preconditions.checkArgument(walletId && cb);
|
||||
this.getGlobal('nameFor::' + walletId, cb);
|
||||
};
|
||||
|
||||
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')
|
||||
continue;
|
||||
|
||||
if (typeof uniq[walletId] === 'undefined') {
|
||||
walletIds.push(walletId);
|
||||
uniq[walletId] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cb(walletIds);
|
||||
});
|
||||
};
|
||||
|
||||
Storage.prototype.getWallets = 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.getName(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.deleteWallet = 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.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) {
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
|
||||
var l = Object.keys(obj).length,
|
||||
i = 0;
|
||||
for (var k in obj) {
|
||||
self.set(walletId, k, obj[k], function() {
|
||||
if (++i == l) {
|
||||
if (obj.opts.name)
|
||||
self.setName(walletId, obj.opts.name, cb);
|
||||
else
|
||||
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;
|
||||
52
js/models/core/PluginManager.js
Normal file
52
js/models/core/PluginManager.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var log = require('../../log');
|
||||
|
||||
function PluginManager(config) {
|
||||
this.registered = {};
|
||||
this.scripts = [];
|
||||
|
||||
for (var ii in config.plugins) {
|
||||
var pluginName = ii;
|
||||
|
||||
if (!config.plugins[pluginName])
|
||||
continue;
|
||||
|
||||
log.info('Loading plugin: ' + pluginName);
|
||||
var pluginClass = require('../plugins/' + pluginName);
|
||||
var pluginObj = new pluginClass(config[pluginName]);
|
||||
pluginObj.init();
|
||||
this._register(pluginObj, pluginName);
|
||||
}
|
||||
};
|
||||
|
||||
var KIND_UNIQUE = PluginManager.KIND_UNIQUE = 1;
|
||||
var KIND_MULTIPLE = PluginManager.KIND_MULTIPLE = 2;
|
||||
|
||||
PluginManager.TYPE = {};
|
||||
PluginManager.TYPE['STORAGE'] = KIND_UNIQUE;
|
||||
|
||||
PluginManager.prototype._register = function(obj, name) {
|
||||
preconditions.checkArgument(obj.type, 'Plugin has not type:' + 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);
|
||||
|
||||
if (kind === PluginManager.KIND_UNIQUE) {
|
||||
this.registered[type] = obj;
|
||||
} else {
|
||||
this.registered[type] = this.registered[type] || [];
|
||||
this.registered[type].push(obj);
|
||||
}
|
||||
|
||||
this.scripts = this.scripts.concat(obj.scripts || []);
|
||||
};
|
||||
|
||||
|
||||
PluginManager.prototype.get = function(type) {
|
||||
return this.registered[type];
|
||||
};
|
||||
|
||||
module.exports = PluginManager;
|
||||
|
|
@ -50,7 +50,6 @@ TxProposals.prototype.getNtxidsSince = function(sinceTs) {
|
|||
if (txp.createdTs >= sinceTs)
|
||||
ret.push(ii);
|
||||
}
|
||||
console.log('[TxProposals.js.52:ret:]',ret); //TODO
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ function Wallet(opts) {
|
|||
'publicKeyRing', 'txProposals', 'privateKey', 'version',
|
||||
'reconnectDelay'
|
||||
].forEach(function(k) {
|
||||
preconditions.checkArgument(!_.isUndefined(opts[k]), 'missing required option for Wallet: ' + k);
|
||||
preconditions.checkArgument(!_.isUndefined(opts[k]), 'MISSOPT: missing required option for Wallet: ' + k);
|
||||
self[k] = opts[k];
|
||||
});
|
||||
|
||||
|
|
@ -822,21 +822,28 @@ Wallet.prototype.getRegisteredPeerIds = function() {
|
|||
* @emits locked - in case the wallet is opened in another instance
|
||||
*/
|
||||
Wallet.prototype.keepAlive = function() {
|
||||
try {
|
||||
this.lock.keepAlive();
|
||||
} catch (e) {
|
||||
log.debug(e);
|
||||
this.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.');
|
||||
}
|
||||
var self = this;
|
||||
|
||||
this.lock.keepAlive(function(err) {
|
||||
if (err) {
|
||||
log.debug(err);
|
||||
self.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Store the wallet's state
|
||||
* @param {function} callback (err)
|
||||
*/
|
||||
Wallet.prototype.store = function() {
|
||||
Wallet.prototype.store = function(cb) {
|
||||
var self = this;
|
||||
this.keepAlive();
|
||||
this.storage.setFromObj(this.id, this.toObj());
|
||||
log.debug('Wallet stored');
|
||||
this.storage.setFromObj(this.id, this.toObj(), function(err) {
|
||||
log.debug('Wallet stored');
|
||||
if (cb)
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -878,7 +885,7 @@ Wallet.prototype.toObj = function() {
|
|||
*/
|
||||
Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||
|
||||
// TODO: What is this supposed to do?
|
||||
// clone opts
|
||||
var opts = JSON.parse(JSON.stringify(o.opts));
|
||||
|
||||
opts.addressBook = o.addressBook;
|
||||
|
|
@ -2342,11 +2349,14 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb)
|
|||
/**
|
||||
* @desc Closes the wallet and disconnects all services
|
||||
*/
|
||||
Wallet.prototype.close = function() {
|
||||
Wallet.prototype.close = function(cb) {
|
||||
var self =this;
|
||||
log.debug('## CLOSING');
|
||||
this.lock.release();
|
||||
this.network.cleanUp();
|
||||
this.blockchain.destroy();
|
||||
this.lock.release(function() {
|
||||
self.network.cleanUp();
|
||||
self.blockchain.destroy();
|
||||
if (cb) return cb();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
'use strict';
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
var TxProposals = require('./TxProposals');
|
||||
var PublicKeyRing = require('./PublicKeyRing');
|
||||
|
|
@ -6,10 +7,11 @@ var PrivateKey = require('./PrivateKey');
|
|||
var Wallet = require('./Wallet');
|
||||
var _ = require('underscore');
|
||||
var log = require('../../log');
|
||||
var PluginManager = require('./PluginManager');
|
||||
var Async = module.exports.Async = require('../network/Async');
|
||||
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
||||
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../storage/LocalEncrypted');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var Storage = module.exports.Storage = require('../Storage');
|
||||
|
||||
/**
|
||||
* @desc
|
||||
|
|
@ -32,15 +34,26 @@ var preconditions = require('preconditions').singleton();
|
|||
* @param {string} version - the version of copay for which this wallet was generated (for example, 0.4.7)
|
||||
* @constructor
|
||||
*/
|
||||
function WalletFactory(config, version) {
|
||||
var self = this;
|
||||
config = config || {};
|
||||
|
||||
this.Storage = config.Storage || StorageLocalEncrypted;
|
||||
function WalletFactory(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;
|
||||
|
||||
this.storage = new this.Storage(config.storage);
|
||||
var storageOpts = {};
|
||||
|
||||
if (pluginManager) {
|
||||
storageOpts = {
|
||||
storage: pluginManager.get('STORAGE')
|
||||
};
|
||||
}
|
||||
|
||||
this.storage = new this.Storage(storageOpts);
|
||||
|
||||
this.networks = {
|
||||
'livenet': new this.Network(config.network.livenet),
|
||||
'testnet': new this.Network(config.network.testnet),
|
||||
|
|
@ -50,31 +63,10 @@ function WalletFactory(config, version) {
|
|||
'testnet': new this.Blockchain(config.network.testnet),
|
||||
};
|
||||
|
||||
this.walletDefaults = config.wallet;
|
||||
this.walletDefaults = config.wallet || {};
|
||||
this.version = version;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Returns true if the storage instance can retrieve the following keys using a given walletId
|
||||
* <ul>
|
||||
* <li><tt>publicKeyRing</tt></li>
|
||||
* <li><tt>txProposals</tt></li>
|
||||
* <li><tt>opts</tt></li>
|
||||
* <li><tt>privateKey</tt></li>
|
||||
* </ul>
|
||||
* @param {string} walletId
|
||||
* @return {boolean} true if all the keys are present in the storage instance
|
||||
*/
|
||||
WalletFactory.prototype._checkRead = function(walletId) {
|
||||
var s = this.storage;
|
||||
var ret =
|
||||
s.get(walletId, 'publicKeyRing') &&
|
||||
s.get(walletId, 'txProposals') &&
|
||||
s.get(walletId, 'opts') &&
|
||||
s.get(walletId, 'privateKey');
|
||||
return !!ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc obtain network name from serialized wallet
|
||||
|
|
@ -94,10 +86,15 @@ WalletFactory.prototype.obtainNetworkName = function(obj) {
|
|||
* @param {string[]} skipFields - fields to skip when importing
|
||||
* @return {Wallet}
|
||||
*/
|
||||
WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
||||
var networkName = this.obtainNetworkName(obj);
|
||||
WalletFactory.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 || [];
|
||||
|
|
@ -117,16 +114,15 @@ WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
|||
/**
|
||||
* @desc Imports a wallet from an encrypted base64 object
|
||||
* @param {string} base64 - the base64 encoded object
|
||||
* @param {string} password - password to decrypt it
|
||||
* @param {string} passphrase - passphrase to decrypt it
|
||||
* @param {string[]} skipFields - fields to ignore when importing
|
||||
* @return {Wallet}
|
||||
*/
|
||||
WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) {
|
||||
this.storage._setPassphrase(password);
|
||||
WalletFactory.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) {
|
||||
this.storage.setPassphrase(passphrase);
|
||||
var walletObj = this.storage.import(base64);
|
||||
if (!walletObj) return false;
|
||||
var w = this.fromObj(walletObj, skipFields);
|
||||
return w;
|
||||
return this.fromObj(walletObj, skipFields);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -134,15 +130,15 @@ WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields
|
|||
* @TODO: this is essentialy the same method as {@link WalletFactory#fromEncryptedObj}!
|
||||
* @desc Imports a wallet from an encrypted base64 object
|
||||
* @param {string} base64 - the base64 encoded object
|
||||
* @param {string} password - password to decrypt it
|
||||
* @param {string} passphrase - passphrase to decrypt it
|
||||
* @param {string[]} skipFields - fields to ignore when importing
|
||||
* @return {Wallet}
|
||||
*/
|
||||
WalletFactory.prototype.import = function(base64, password, skipFields) {
|
||||
WalletFactory.prototype.import = function(base64, passphrase, skipFields) {
|
||||
var self = this;
|
||||
var w = self.fromEncryptedObj(base64, password, skipFields);
|
||||
var w = self.fromEncryptedObj(base64, passphrase, skipFields);
|
||||
|
||||
if (!w) throw new Error('Wrong password');
|
||||
if (!w) throw new Error('Wrong passphrase');
|
||||
return w;
|
||||
};
|
||||
|
||||
|
|
@ -150,26 +146,52 @@ WalletFactory.prototype.import = function(base64, password, skipFields) {
|
|||
* @desc Retrieve a wallet from storage
|
||||
* @param {string} walletId - the wallet id
|
||||
* @param {string[]} skipFields - parameters to ignore when importing
|
||||
* @return {Wallet}
|
||||
* @param {function} callback - {err, Wallet}
|
||||
*/
|
||||
WalletFactory.prototype.read = function(walletId, skipFields) {
|
||||
if (!this._checkRead(walletId))
|
||||
return false;
|
||||
|
||||
WalletFactory.prototype.read = function(walletId, skipFields, cb) {
|
||||
var self = this,
|
||||
err;
|
||||
var obj = {};
|
||||
var s = this.storage;
|
||||
|
||||
obj.id = walletId;
|
||||
_.each(Wallet.PERSISTED_PROPERTIES, function(value) {
|
||||
obj[value] = s.get(walletId, value);
|
||||
this.storage.getMany(walletId, Wallet.PERSISTED_PROPERTIES, function(ret) {
|
||||
for (var ii in ret) {
|
||||
obj[ii] = ret[ii];
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
var w = this.fromObj(obj, skipFields);
|
||||
return w;
|
||||
|
||||
/**
|
||||
* @desc This method instantiates a wallet. Usefull for stubbing.
|
||||
*
|
||||
* @param {opts} opts, ready for new Wallet(opts)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
WalletFactory.prototype._getWallet = function(opts) {
|
||||
return new Wallet(opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc This method instantiates a wallet
|
||||
* @desc This method prepares options for a new Wallet
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.id
|
||||
|
|
@ -185,9 +207,11 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
|
|||
* @TODO: Figure out in what unit is this reconnect delay.
|
||||
* @param {number} opts.reconnectDelay milliseconds?
|
||||
* @param {number=} opts.version
|
||||
* @param {callback} opts.version
|
||||
* @return {Wallet}
|
||||
*/
|
||||
WalletFactory.prototype.create = function(opts) {
|
||||
WalletFactory.prototype.create = function(opts, cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
opts = opts || {};
|
||||
opts.networkName = opts.networkName || 'testnet';
|
||||
|
|
@ -224,7 +248,6 @@ WalletFactory.prototype.create = function(opts) {
|
|||
});
|
||||
log.debug('\t### TxProposals Initialized');
|
||||
|
||||
this.storage._setPassphrase(opts.passphrase);
|
||||
|
||||
opts.storage = this.storage;
|
||||
opts.network = this.networks[opts.networkName];
|
||||
|
|
@ -236,10 +259,15 @@ WalletFactory.prototype.create = function(opts) {
|
|||
opts.totalCopayers = totalCopayers;
|
||||
opts.version = opts.version || this.version;
|
||||
|
||||
var w = new Wallet(opts);
|
||||
w.store();
|
||||
this.storage.setLastOpened(w.id);
|
||||
return w;
|
||||
this.storage.setPassphrase(opts.passphrase);
|
||||
var w = this._getWallet(opts);
|
||||
var self = this;
|
||||
w.store(function(err) {
|
||||
if (err) return cb(err);
|
||||
self.storage.setLastOpened(w.id, function(err) {
|
||||
return cb(err, w);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -266,29 +294,31 @@ WalletFactory.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
|
||||
* @return {Wallet}
|
||||
* @param {function} callback (err, {Wallet})
|
||||
* @return
|
||||
*/
|
||||
WalletFactory.prototype.open = function(walletId, passphrase) {
|
||||
this.storage._setPassphrase(passphrase);
|
||||
var w = this.read(walletId);
|
||||
if (w) {
|
||||
w.store();
|
||||
}
|
||||
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);
|
||||
|
||||
this.storage.setLastOpened(walletId);
|
||||
return w;
|
||||
w.store(function(err) {
|
||||
self.storage.setLastOpened(walletId, function() {
|
||||
return cb(err, w);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Retrieve all wallets stored without encription in the storage instance
|
||||
* @returns {Wallet[]}
|
||||
*/
|
||||
WalletFactory.prototype.getWallets = function() {
|
||||
var ret = this.storage.getWallets();
|
||||
ret.forEach(function(i) {
|
||||
i.show = i.name ? ((i.name + ' <' + i.id + '>')) : i.id;
|
||||
WalletFactory.prototype.getWallets = function(cb) {
|
||||
this.storage.getWallets(function(ret) {
|
||||
ret.forEach(function(i) {
|
||||
i.show = i.name ? ((i.name + ' <' + i.id + '>')) : i.id;
|
||||
});
|
||||
return cb(null, ret);
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -301,9 +331,12 @@ WalletFactory.prototype.getWallets = function() {
|
|||
*/
|
||||
WalletFactory.prototype.delete = function(walletId, cb) {
|
||||
var s = this.storage;
|
||||
s.deleteWallet(walletId);
|
||||
s.setLastOpened(undefined);
|
||||
return cb();
|
||||
s.deleteWallet(walletId, function(err) {
|
||||
if (err) return cb(err);
|
||||
s.setLastOpened(null, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -331,15 +364,21 @@ WalletFactory.prototype.decodeSecret = function(secret) {
|
|||
* information locally using <tt>passphrase</tt>. <tt>privateHex</tt> is the
|
||||
* private extended master key. <tt>cb</tt> has two params: error and wallet.
|
||||
*
|
||||
* @param {string} secret - the wallet secret
|
||||
* @param {string} nickname - a nickname for the current user
|
||||
* @param {string} passphrase - a passphrase to use to encrypt the wallet for persistance
|
||||
* @param {string} privateHex - the private extended master key
|
||||
* @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.nickname - a nickname for the current user
|
||||
* @param {string} opts.privateHex - the private extended master key
|
||||
* @param {walletCreationCallback} cb - a callback
|
||||
*/
|
||||
WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) {
|
||||
WalletFactory.prototype.joinCreateSession = function(opts, cb) {
|
||||
preconditions.checkArgument(opts);
|
||||
preconditions.checkArgument(opts.secret);
|
||||
preconditions.checkArgument(opts.passphrase);
|
||||
preconditions.checkArgument(opts.nickname);
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
var decodedSecret = this.decodeSecret(secret);
|
||||
var decodedSecret = this.decodeSecret(opts.secret);
|
||||
if (!decodedSecret || !decodedSecret.networkName || !decodedSecret.pubKey) {
|
||||
return cb('badSecret');
|
||||
}
|
||||
|
|
@ -348,14 +387,14 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
|||
networkName: decodedSecret.networkName,
|
||||
};
|
||||
|
||||
if (privateHex && privateHex.length > 1) {
|
||||
if (opts.privateHex && opts.privateHex.length > 1) {
|
||||
privOpts.extendedPrivateKeyString = privateHex;
|
||||
}
|
||||
|
||||
//Create our PrivateK
|
||||
var privateKey = new PrivateKey(privOpts);
|
||||
log.debug('\t### PrivateKey Initialized');
|
||||
var opts = {
|
||||
var joinOpts = {
|
||||
copayerId: privateKey.getId(),
|
||||
privkey: privateKey.getIdPriv(),
|
||||
key: privateKey.getIdKey(),
|
||||
|
|
@ -379,23 +418,32 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
|||
return cb('joinError');
|
||||
});
|
||||
|
||||
joinNetwork.start(opts, function() {
|
||||
joinNetwork.greet(decodedSecret.pubKey, opts.secretNumber);
|
||||
joinNetwork.start(joinOpts, function() {
|
||||
|
||||
joinNetwork.greet(decodedSecret.pubKey, joinOpts.secretNumber);
|
||||
joinNetwork.on('data', function(sender, data) {
|
||||
if (data.type === 'walletId') {
|
||||
if (data.type === 'walletId' && data.opts) {
|
||||
if (data.networkName !== decodedSecret.networkName) {
|
||||
return cb('badNetwork');
|
||||
}
|
||||
|
||||
data.opts.privateKey = privateKey;
|
||||
data.opts.nickname = nickname;
|
||||
data.opts.passphrase = passphrase;
|
||||
data.opts.id = data.walletId;
|
||||
var w = self.create(data.opts);
|
||||
w.sendWalletReady(decodedSecret.pubKey);
|
||||
return cb(null, w);
|
||||
} else {
|
||||
return cb('walletFull', w);
|
||||
var walletOpts = _.clone(data.opts);
|
||||
walletOpts.id = data.walletId;
|
||||
|
||||
walletOpts.privateKey = privateKey;
|
||||
walletOpts.nickname = opts.nickname;
|
||||
walletOpts.passphrase = opts.passphrase;
|
||||
|
||||
self.create(walletOpts, function(err, w) {
|
||||
|
||||
if (w) {
|
||||
w.sendWalletReady(decodedSecret.pubKey);
|
||||
} else {
|
||||
if (!err) err = 'walletFull';
|
||||
log.info(err);
|
||||
}
|
||||
return cb(err, w);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,50 +6,95 @@ function WalletLock(storage, walletId, timeoutMin) {
|
|||
preconditions.checkArgument(storage);
|
||||
preconditions.checkArgument(walletId);
|
||||
|
||||
this.sessionId = storage.getSessionId();
|
||||
this.storage = storage;
|
||||
this.timeoutMin = timeoutMin || 5;
|
||||
this.key = WalletLock._keyFor(walletId);
|
||||
this._setLock();
|
||||
}
|
||||
|
||||
|
||||
WalletLock.prototype.init = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
|
||||
self.storage.getSessionId(function(sid) {
|
||||
preconditions.checkState(sid);
|
||||
|
||||
self.sessionId = sid;
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
WalletLock._keyFor = function(walletId) {
|
||||
return 'lock' + '::' + walletId;
|
||||
};
|
||||
|
||||
WalletLock.prototype._isLockedByOther = function() {
|
||||
var json = this.storage.getGlobal(this.key);
|
||||
var wl = json ? JSON.parse(json) : null;
|
||||
var t = wl ? (Date.now() - wl.expireTs) : false;
|
||||
// is not locked?
|
||||
if (!wl || t > 0 || wl.sessionId === this.sessionId)
|
||||
return false;
|
||||
WalletLock.prototype._isLockedByOther = function(cb) {
|
||||
var self = this;
|
||||
|
||||
// Seconds remainding
|
||||
return parseInt(-t/1000.);
|
||||
};
|
||||
this.storage.getGlobal(this.key, function(json) {
|
||||
var wl = json ? JSON.parse(json) : null;
|
||||
if (!wl || !wl.expireTs)
|
||||
return cb(false);
|
||||
|
||||
var expiredSince = Date.now() - wl.expireTs;
|
||||
if (expiredSince >= 0)
|
||||
return cb(false);
|
||||
|
||||
WalletLock.prototype._setLock = function() {
|
||||
this.storage.setGlobal(this.key, {
|
||||
sessionId: this.sessionId,
|
||||
expireTs: Date.now() + this.timeoutMin * 60 * 1000,
|
||||
var isMyself = wl.sessionId === self.sessionId;
|
||||
|
||||
if (isMyself)
|
||||
return cb(false);
|
||||
|
||||
// Seconds remainding
|
||||
return cb(parseInt(-expiredSince / 1000));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype.keepAlive = function() {
|
||||
WalletLock.prototype._setLock = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
preconditions.checkState(this.sessionId);
|
||||
var self = this;
|
||||
|
||||
var t = this._isLockedByOther();
|
||||
if (t)
|
||||
throw new Error('Wallet is already open. Close it to proceed or wait '+ t + ' seconds if you close it already' );
|
||||
this._setLock();
|
||||
this.storage.setGlobal(this.key, {
|
||||
sessionId: this.sessionId,
|
||||
expireTs: Date.now() + this.timeoutMin * 60 * 1000,
|
||||
}, function() {
|
||||
|
||||
cb(null);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype.release = function() {
|
||||
this.storage.removeGlobal(this.key);
|
||||
WalletLock.prototype._doKeepAlive = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
preconditions.checkState(this.sessionId);
|
||||
|
||||
var self = this;
|
||||
|
||||
this._isLockedByOther(function(t) {
|
||||
if (t)
|
||||
return cb(new Error('LOCKED: Wallet is locked for ' + t + ' srcs'));
|
||||
|
||||
self._setLock(cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
WalletLock.prototype.keepAlive = function(cb) {
|
||||
var self = this;
|
||||
|
||||
if (!self.sessionId) {
|
||||
return self.init(self._doKeepAlive.bind(self, cb));
|
||||
};
|
||||
|
||||
return this._doKeepAlive(cb);
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype.release = function(cb) {
|
||||
this.storage.removeGlobal(this.key, cb);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,208 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var CryptoJS = require('node-cryptojs-aes').CryptoJS;
|
||||
var bitcore = require('bitcore');
|
||||
var preconditions = require('preconditions').instance();
|
||||
var id = 0;
|
||||
|
||||
function Storage(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
this.__uniqueid = ++id;
|
||||
if (opts.password)
|
||||
this._setPassphrase(opts.password);
|
||||
|
||||
try {
|
||||
this.localStorage = opts.localStorage || localStorage;
|
||||
this.sessionStorage = opts.sessionStorage || sessionStorage;
|
||||
} catch (e) {}
|
||||
preconditions.checkState(this.localStorage, 'No localstorage found');
|
||||
preconditions.checkState(this.sessionStorage, 'No sessionStorage found');
|
||||
}
|
||||
|
||||
var pps = {};
|
||||
Storage.prototype._getPassphrase = function() {
|
||||
if (!pps[this.__uniqueid])
|
||||
throw new Error('No passprase 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) {
|
||||
var ret;
|
||||
ret = this.localStorage.getItem(k);
|
||||
if (!ret) return null;
|
||||
ret = this._decrypt(ret);
|
||||
if (!ret) return null;
|
||||
ret = ret.toString(CryptoJS.enc.Utf8);
|
||||
ret = JSON.parse(ret);
|
||||
return ret;
|
||||
};
|
||||
|
||||
Storage.prototype._write = function(k, v) {
|
||||
v = JSON.stringify(v);
|
||||
v = this._encrypt(v);
|
||||
|
||||
this.localStorage.setItem(k, v);
|
||||
};
|
||||
|
||||
// get value by key
|
||||
Storage.prototype.getGlobal = function(k) {
|
||||
var item = this.localStorage.getItem(k);
|
||||
return item == 'undefined' ? undefined : item;
|
||||
};
|
||||
|
||||
// set value for key
|
||||
Storage.prototype.setGlobal = function(k, v) {
|
||||
this.localStorage.setItem(k, typeof v === 'object' ? JSON.stringify(v) : v);
|
||||
};
|
||||
|
||||
// remove value for key
|
||||
Storage.prototype.removeGlobal = function(k) {
|
||||
this.localStorage.removeItem(k);
|
||||
};
|
||||
|
||||
Storage.prototype.getSessionId = function() {
|
||||
var sessionId = this.sessionStorage.getItem('sessionId');
|
||||
if (!sessionId) {
|
||||
sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex');
|
||||
this.sessionStorage.setItem('sessionId', sessionId);
|
||||
}
|
||||
return sessionId;
|
||||
};
|
||||
|
||||
Storage.prototype._key = function(walletId, k) {
|
||||
return walletId + '::' + k;
|
||||
};
|
||||
// get value by key
|
||||
Storage.prototype.get = function(walletId, k) {
|
||||
var ret = this._read(this._key(walletId, k));
|
||||
return ret;
|
||||
};
|
||||
|
||||
// set value for key
|
||||
Storage.prototype.set = function(walletId, k, v) {
|
||||
this._write(this._key(walletId, k), v);
|
||||
};
|
||||
|
||||
// remove value for key
|
||||
Storage.prototype.remove = function(walletId, k) {
|
||||
this.removeGlobal(this._key(walletId, k));
|
||||
};
|
||||
|
||||
Storage.prototype.setName = function(walletId, name) {
|
||||
this.setGlobal('nameFor::' + walletId, name);
|
||||
};
|
||||
|
||||
Storage.prototype.getName = function(walletId) {
|
||||
var ret = this.getGlobal('nameFor::' + walletId);
|
||||
return ret;
|
||||
};
|
||||
|
||||
Storage.prototype.getWalletIds = function() {
|
||||
var walletIds = [];
|
||||
var uniq = {};
|
||||
|
||||
for (var i = 0; i < this.localStorage.length; i++) {
|
||||
var key = this.localStorage.key(i);
|
||||
var split = key.split('::');
|
||||
if (split.length == 2) {
|
||||
var walletId = split[0];
|
||||
|
||||
if (!walletId || walletId === 'nameFor' || walletId === 'lock')
|
||||
continue;
|
||||
|
||||
if (typeof uniq[walletId] === 'undefined') {
|
||||
walletIds.push(walletId);
|
||||
uniq[walletId] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return walletIds;
|
||||
};
|
||||
|
||||
Storage.prototype.getWallets = function() {
|
||||
var wallets = [];
|
||||
var ids = this.getWalletIds();
|
||||
|
||||
for (var i in ids) {
|
||||
wallets.push({
|
||||
id: ids[i],
|
||||
name: this.getName(ids[i]),
|
||||
});
|
||||
}
|
||||
return wallets;
|
||||
};
|
||||
|
||||
Storage.prototype.deleteWallet = function(walletId) {
|
||||
var toDelete = {};
|
||||
toDelete['nameFor::' + walletId] = 1;
|
||||
|
||||
for (var i = 0; i < this.localStorage.length; i++) {
|
||||
var key = this.localStorage.key(i);
|
||||
var split = key.split('::');
|
||||
if (split.length == 2 && split[0] === walletId) {
|
||||
toDelete[key] = 1;
|
||||
}
|
||||
}
|
||||
for (var i in toDelete) {
|
||||
this.removeGlobal(i);
|
||||
}
|
||||
};
|
||||
|
||||
Storage.prototype.setLastOpened = function(walletId) {
|
||||
this.setGlobal('lastOpened', walletId);
|
||||
}
|
||||
|
||||
Storage.prototype.getLastOpened = function() {
|
||||
return this.getGlobal('lastOpened');
|
||||
}
|
||||
|
||||
//obj contains keys to be set
|
||||
Storage.prototype.setFromObj = function(walletId, obj) {
|
||||
for (var k in obj) {
|
||||
this.set(walletId, k, obj[k]);
|
||||
}
|
||||
this.setName(walletId, obj.opts.name);
|
||||
};
|
||||
|
||||
// remove all values
|
||||
Storage.prototype.clearAll = function() {
|
||||
this.localStorage.clear();
|
||||
};
|
||||
|
||||
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;
|
||||
|
|
@ -76,8 +76,8 @@ angular
|
|||
// IDLE timeout
|
||||
var timeout = config.wallet.idleDurationMin * 60 || 300;
|
||||
$idleProvider.idleDuration(timeout); // in seconds
|
||||
$idleProvider.warningDuration(20); // in seconds
|
||||
$keepaliveProvider.interval(2); // in seconds
|
||||
$idleProvider.warningDuration(40); // in seconds
|
||||
$keepaliveProvider.interval(30); // in seconds
|
||||
})
|
||||
.run(function($rootScope, $location, $idle, gettextCatalog) {
|
||||
gettextCatalog.currentLanguage = config.defaultLanguage;
|
||||
|
|
|
|||
18
js/services/pluginManager.js
Normal file
18
js/services/pluginManager.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('pluginManager', function(angularLoad){
|
||||
var pm = new copay.PluginManager(config);
|
||||
var scripts = pm.scripts;
|
||||
|
||||
for(var ii in scripts){
|
||||
var src = scripts[ii].src;
|
||||
|
||||
console.log('\tLoading ',src); //TODO
|
||||
angularLoad.loadScript(src)
|
||||
.then(scripts[ii].then || null)
|
||||
.catch(function() {
|
||||
throw new Error('Loading ' + src);
|
||||
})
|
||||
}
|
||||
return pm;
|
||||
});
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services').factory('walletFactory', function(pluginManager){
|
||||
return new copay.WalletFactory(config, copay.version, pluginManager);
|
||||
});
|
||||
|
||||
angular.module('copayApp.services').value('walletFactory', new copay.WalletFactory(config, copay.version));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue