Browse Source

store cached data in db

activeAddress
Ivan Socolsky 9 years ago
parent
commit
94a376ca33
  1. 42
      lib/server.js
  2. 36
      lib/storage.js
  3. 113
      test/integration/helpers.js
  4. 124
      test/integration/server.js

42
lib/server.js

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

36
lib/storage.js

@ -20,6 +20,7 @@ var collections = {
COPAYERS_LOOKUP: 'copayers_lookup', COPAYERS_LOOKUP: 'copayers_lookup',
PREFERENCES: 'preferences', PREFERENCES: 'preferences',
EMAIL_QUEUE: 'email_queue', EMAIL_QUEUE: 'email_queue',
CACHE: 'cache',
}; };
var Storage = function(opts) { var Storage = function(opts) {
@ -56,6 +57,10 @@ Storage.prototype._createIndexes = function() {
this.db.collection(collections.EMAIL_QUEUE).createIndex({ this.db.collection(collections.EMAIL_QUEUE).createIndex({
notificationId: 1, notificationId: 1,
}); });
this.db.collection(collections.CACHE).createIndex({
walletId: 1,
key: 1,
});
}; };
Storage.prototype.connect = function(opts, cb) { 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) { Storage.prototype._dump = function(cb, fn) {
fn = fn || console.log; fn = fn || console.log;
cb = cb || function() {}; cb = cb || function() {};

113
test/integration/helpers.js

@ -211,52 +211,77 @@ helpers.toSatoshi = function(btc) {
} }
}; };
helpers.stubUtxos = function(server, wallet, amounts, cb) { helpers.stubUtxos = function(server, wallet, amounts, opts, cb) {
async.mapSeries(_.range(0, amounts.length > 2 ? 2 : 1), function(i, next) { if (_.isFunction(opts)) {
server.createAddress({}, next); cb = opts;
}, function(err, addresses) { opts = {};
should.not.exist(err); }
addresses.should.not.be.empty; opts = opts || {};
var utxos = _.compact(_.map([].concat(amounts), function(amount, i) {
var confirmations; if (!helpers._utxos) helpers._utxos = {};
if (_.isString(amount) && _.startsWith(amount, 'u')) {
amount = parseFloat(amount.substring(1)); async.waterfall([
confirmations = 0;
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 { } else {
confirmations = Math.floor(Math.random() * 100 + 1); helpers._utxos = utxos;
} }
if (amount <= 0) return null;
blockchainExplorer.getUnspentUtxos = function(addresses, cb) {
var address = addresses[i % addresses.length]; var selected = _.filter(helpers._utxos, function(utxo) {
return _.contains(addresses, utxo.address);
var scriptPubKey; });
switch (wallet.addressType) { return cb(null, selected);
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(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) { helpers.createAddresses = function(server, wallet, main, change, cb) {
var clock = sinon.useFakeTimers(Date.now(), 'Date'); 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); clock.tick(1000);
var address = wallet.createAddress(i >= main); var address = wallet.createAddress(i >= main);
server.storage.storeAddressAndWallet(wallet, address, function(err) { server.storage.storeAddressAndWallet(wallet, address, function(err) {
next(err, address); next(err, address);
}); });
}, function(err, addresses) { }, function(err, addresses) {
if (err) throw new Error('Could not generate addresses'); should.not.exist(err);
clock.restore(); clock.restore();
return cb(_.take(addresses, main), _.takeRight(addresses, change)); return cb(_.take(addresses, main), _.takeRight(addresses, change));
}); });

124
test/integration/server.js

@ -1504,7 +1504,7 @@ describe('Wallet service', function() {
}); });
}); });
describe('#getBalance 2 steps', function() { describe.only('#getBalance 2 steps', function() {
var server, wallet; var server, wallet;
beforeEach(function(done) { beforeEach(function(done) {
helpers.createAndJoinWallet(1, 1, function(s, w) { 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() { it('should trigger notification when balance of non-prioritary addresses is updated', function(done) {
server.getBalance2Steps({}, function(err, balance) { var addresses;
should.not.exist(err);
should.exist(balance); async.series([
//balance.totalAmount.should.equal(helpers.toSatoshi(1.1));
//done(); 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();
}); });
}); });
}); });

Loading…
Cancel
Save