store cached data in db

This commit is contained in:
Ivan Socolsky 2015-12-09 15:09:47 -03:00
parent 3874d14f71
commit 94a376ca33
4 changed files with 238 additions and 75 deletions

View File

@ -992,8 +992,6 @@ WalletService.prototype._getBalanceFromAddresses = function(addresses, cb) {
});
};
var prioritaryAddresses;
/**
* Get wallet balance.
* @param {Object} opts
@ -1009,11 +1007,15 @@ WalletService.prototype.getBalance = function(opts, cb) {
// Update cache
var addressIndex = _.indexBy(addresses, 'address');
prioritaryAddresses = _.map(_.pluck(balance.byAddress, 'address'), function(addrStr) {
var freshAddresses = _.map(_.pluck(balance.byAddress, 'address'), function(addrStr) {
return addressIndex[addrStr];
});
return cb(null, balance);
self.storage.storeCacheData(self.walletId, 'freshAddresses', freshAddresses, function(err) {
if (err) {
log.warn('Could not update wallet cache', err);
}
return cb(null, balance);
});
});
});
};
@ -1027,20 +1029,24 @@ WalletService.prototype.getBalance = function(opts, cb) {
WalletService.prototype.getBalance2Steps = function(opts, cb) {
var self = this;
if (!prioritaryAddresses) return self.getBalance(opts, cb);
self._getBalanceFromAddresses(prioritaryAddresses, function(err, partialBalance) {
if (err) return cb(err);
cb(null, partialBalance);
setTimeout(function() {
self.getBalance(opts, function(err, fullBalance) {
if (err) return;
if (!_.isEqual(partialBalance, fullBalance)) {
console.log('*** [server.js ln1015] ACTUALIZAR BALANCE!!!!, partialBalance, fullBalance:', partialBalance, fullBalance); // TODO
}
self.storage.fetchCacheData(self.walletId, 'freshAddresses', function(err, freshAddresses) {
if (err || _.isEmpty(freshAddresses)) {
return self.getBalance(opts, cb);
} else {
self._getBalanceFromAddresses(freshAddresses, function(err, partialBalance) {
if (err) return cb(err);
cb(null, partialBalance);
setTimeout(function() {
self.getBalance(opts, function(err, fullBalance) {
if (err) return;
if (!_.isEqual(partialBalance, fullBalance)) {
self._notify('BalanceUpdated', fullBalance);
}
});
}, 1);
return;
});
}, 1);
return;
}
});
};

View File

@ -20,6 +20,7 @@ var collections = {
COPAYERS_LOOKUP: 'copayers_lookup',
PREFERENCES: 'preferences',
EMAIL_QUEUE: 'email_queue',
CACHE: 'cache',
};
var Storage = function(opts) {
@ -56,6 +57,10 @@ Storage.prototype._createIndexes = function() {
this.db.collection(collections.EMAIL_QUEUE).createIndex({
notificationId: 1,
});
this.db.collection(collections.CACHE).createIndex({
walletId: 1,
key: 1,
});
};
Storage.prototype.connect = function(opts, cb) {
@ -501,6 +506,37 @@ Storage.prototype.fetchEmailByNotification = function(notificationId, cb) {
});
};
Storage.prototype.storeCacheData = function(walletId, key, value, cb) {
var self = this;
var record = {
walletId: walletId,
key: key,
data: value
};
self.db.collection(collections.CACHE).update({
walletId: walletId,
key: key,
}, record, {
w: 1,
upsert: true,
}, cb);
};
Storage.prototype.fetchCacheData = function(walletId, key, cb) {
var self = this;
self.db.collection(collections.CACHE).findOne({
walletId: walletId,
key: key,
}, function(err, result) {
if (err) return cb(err);
if (!result) return cb();
return cb(null, result.data);
});
};
Storage.prototype._dump = function(cb, fn) {
fn = fn || console.log;
cb = cb || function() {};

View File

@ -211,52 +211,77 @@ helpers.toSatoshi = function(btc) {
}
};
helpers.stubUtxos = function(server, wallet, amounts, cb) {
async.mapSeries(_.range(0, amounts.length > 2 ? 2 : 1), function(i, next) {
server.createAddress({}, next);
}, function(err, addresses) {
should.not.exist(err);
addresses.should.not.be.empty;
var utxos = _.compact(_.map([].concat(amounts), function(amount, i) {
var confirmations;
if (_.isString(amount) && _.startsWith(amount, 'u')) {
amount = parseFloat(amount.substring(1));
confirmations = 0;
helpers.stubUtxos = function(server, wallet, amounts, opts, cb) {
if (_.isFunction(opts)) {
cb = opts;
opts = {};
}
opts = opts || {};
if (!helpers._utxos) helpers._utxos = {};
async.waterfall([
function(next) {
if (opts.addresses) return next(null, [].concat(opts.addresses));
async.mapSeries(_.range(0, amounts.length > 2 ? 2 : 1), function(i, next) {
server.createAddress({}, next);
}, next);
},
function(addresses, next) {
addresses.should.not.be.empty;
var utxos = _.compact(_.map([].concat(amounts), function(amount, i) {
var confirmations;
if (_.isString(amount) && _.startsWith(amount, 'u')) {
amount = parseFloat(amount.substring(1));
confirmations = 0;
} else {
confirmations = Math.floor(Math.random() * 100 + 1);
}
if (amount <= 0) return null;
var address = addresses[i % addresses.length];
var scriptPubKey;
switch (wallet.addressType) {
case Constants.SCRIPT_TYPES.P2SH:
scriptPubKey = Bitcore.Script.buildMultisigOut(address.publicKeys, wallet.m).toScriptHashOut();
break;
case Constants.SCRIPT_TYPES.P2PKH:
scriptPubKey = Bitcore.Script.buildPublicKeyHashOut(address.address);
break;
}
should.exist(scriptPubKey);
return {
txid: helpers.randomTXID(),
vout: Math.floor(Math.random() * 10 + 1),
satoshis: helpers.toSatoshi(amount),
scriptPubKey: scriptPubKey.toBuffer().toString('hex'),
address: address.address,
confirmations: confirmations
};
}));
if (opts.keepUtxos) {
helpers._utxos = helpers._utxos.concat(utxos);
} else {
confirmations = Math.floor(Math.random() * 100 + 1);
helpers._utxos = utxos;
}
if (amount <= 0) return null;
var address = addresses[i % addresses.length];
var scriptPubKey;
switch (wallet.addressType) {
case Constants.SCRIPT_TYPES.P2SH:
scriptPubKey = Bitcore.Script.buildMultisigOut(address.publicKeys, wallet.m).toScriptHashOut();
break;
case Constants.SCRIPT_TYPES.P2PKH:
scriptPubKey = Bitcore.Script.buildPublicKeyHashOut(address.address);
break;
}
should.exist(scriptPubKey);
return {
txid: helpers.randomTXID(),
vout: Math.floor(Math.random() * 10 + 1),
satoshis: helpers.toSatoshi(amount),
scriptPubKey: scriptPubKey.toBuffer().toString('hex'),
address: address.address,
confirmations: confirmations
blockchainExplorer.getUnspentUtxos = function(addresses, cb) {
var selected = _.filter(helpers._utxos, function(utxo) {
return _.contains(addresses, utxo.address);
});
return cb(null, selected);
};
}));
blockchainExplorer.getUnspentUtxos = function(addresses, cb) {
var selected = _.filter(utxos, function(utxo) {
return _.contains(addresses, utxo.address);
});
return cb(null, selected);
};
return cb(utxos);
return next();
},
], function(err) {
should.not.exist(err);
return cb(helpers._utxos);
});
};
@ -451,14 +476,14 @@ helpers.createProposalOpts = function(type, outputs, signingKey, moreOpts, input
};
helpers.createAddresses = function(server, wallet, main, change, cb) {
var clock = sinon.useFakeTimers(Date.now(), 'Date');
async.map(_.range(main + change), function(i, next) {
async.mapSeries(_.range(main + change), function(i, next) {
clock.tick(1000);
var address = wallet.createAddress(i >= main);
server.storage.storeAddressAndWallet(wallet, address, function(err) {
next(err, address);
});
}, function(err, addresses) {
if (err) throw new Error('Could not generate addresses');
should.not.exist(err);
clock.restore();
return cb(_.take(addresses, main), _.takeRight(addresses, change));
});

View File

@ -1504,7 +1504,7 @@ describe('Wallet service', function() {
});
});
describe('#getBalance 2 steps', function() {
describe.only('#getBalance 2 steps', function() {
var server, wallet;
beforeEach(function(done) {
helpers.createAndJoinWallet(1, 1, function(s, w) {
@ -1536,22 +1536,118 @@ describe('Wallet service', function() {
});
});
});
it.only('should trigger notification when balance of non-prioritary addresses is updated', function(done) {
helpers.stubUtxos(server, wallet, [1, 2], function() {
server.getBalance2Steps({}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3));
helpers.stubUtxos(server, wallet, [0.5, 0.6], function() {
server.getBalance2Steps({}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
//balance.totalAmount.should.equal(helpers.toSatoshi(1.1));
//done();
it('should trigger notification when balance of non-prioritary addresses is updated', function(done) {
var addresses;
async.series([
function(next) {
helpers.createAddresses(server, wallet, 4, 0, function(addrs) {
addresses = addrs;
helpers.stubUtxos(server, wallet, [1, 2], {
addresses: _.take(addresses, 2),
}, function() {
next();
});
});
});
},
function(next) {
server.getBalance2Steps({}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3));
next();
});
},
function(next) {
helpers.stubUtxos(server, wallet, 0.5, {
addresses: addresses[2],
keepUtxos: true,
}, function() {
next();
});
},
function(next) {
server.getBalance2Steps({}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3));
next();
});
},
function(next) {
setTimeout(next, 100);
},
function(next) {
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
var last = _.last(notifications);
last.type.should.equal('BalanceUpdated');
var balance = last.data;
balance.totalAmount.should.equal(helpers.toSatoshi(3.5));
next();
});
},
], function(err) {
should.not.exist(err);
done();
});
});
it('should not trigger notification when only balance of prioritary addresses is updated', function(done) {
var addresses;
async.series([
function(next) {
helpers.createAddresses(server, wallet, 4, 0, function(addrs) {
addresses = addrs;
helpers.stubUtxos(server, wallet, [1, 2], {
addresses: _.take(addresses, 2),
}, function() {
next();
});
});
},
function(next) {
server.getBalance2Steps({}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3));
next();
});
},
function(next) {
helpers.stubUtxos(server, wallet, 0.5, {
addresses: addresses[0],
keepUtxos: true,
}, function() {
next();
});
},
function(next) {
server.getBalance2Steps({}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3.5));
next();
});
},
function(next) {
setTimeout(next, 100);
},
function(next) {
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
var last = _.last(notifications);
last.type.should.not.equal('BalanceUpdated');
next();
});
},
], function(err) {
should.not.exist(err);
done();
});
});
});