Merge pull request #611 from isocolsky/has-balance
WIP Active addresses for balance
This commit is contained in:
commit
5620b79ea6
|
@ -112,4 +112,6 @@ Defaults.RateLimit = {
|
|||
|
||||
Defaults.MAX_REORG_DEPTH = 32;
|
||||
|
||||
Defaults.RECENT_ADDRESS_TIMESPAN = 7 * 24 * 3600; // TXs within the last 7 days are considered recent
|
||||
|
||||
module.exports = Defaults;
|
||||
|
|
|
@ -25,6 +25,7 @@ Address.create = function(opts) {
|
|||
x.type = opts.type || Constants.SCRIPT_TYPES.P2SH;
|
||||
x.hasActivity = undefined;
|
||||
x.lastUsedOn = now;
|
||||
x.hasBalance = false;
|
||||
return x;
|
||||
};
|
||||
|
||||
|
@ -42,6 +43,7 @@ Address.fromObj = function(obj) {
|
|||
x.type = obj.type || Constants.SCRIPT_TYPES.P2SH;
|
||||
x.hasActivity = obj.hasActivity;
|
||||
x.lastUsedOn = obj.lastUsedOn;
|
||||
x.hasBalance = obj.hasBalance;
|
||||
return x;
|
||||
};
|
||||
|
||||
|
|
|
@ -1139,11 +1139,49 @@ WalletService.prototype._getBalanceFromAddresses = function(addresses, cb) {
|
|||
WalletService.prototype.getBalance = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
self.storage.fetchAddresses(self.walletId, function(err, addresses) {
|
||||
function getActiveAddresses(cb) {
|
||||
async.parallel([
|
||||
|
||||
function(done) {
|
||||
self.storage.fetchRecentAddresses(self.walletId, (Date.now() / 1000) - Defaults.RECENT_ADDRESS_TIMESPAN, done);
|
||||
},
|
||||
function(done) {
|
||||
self.storage.fetchAddressesWithBalance(self.walletId, done);
|
||||
}
|
||||
], function(err, res) {
|
||||
return cb(err, res[0].concat(res[1]));
|
||||
})
|
||||
};
|
||||
|
||||
getActiveAddresses(function(err, addresses) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self._getBalanceFromAddresses(addresses, function(err, balance) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, balance);
|
||||
|
||||
// TODO: return as soon as the balance is known and udpate addresses in the background
|
||||
|
||||
// Update active addresses
|
||||
async.waterfall([
|
||||
|
||||
function(next) {
|
||||
self.storage.fetchAddressesWithBalance(self.walletId, function(err, res) {
|
||||
if (err) return next(err);
|
||||
return next(null, _.pluck(res, 'address'));
|
||||
});
|
||||
},
|
||||
function(previous, next) {
|
||||
var current = _.pluck(balance.byAddress, 'address');
|
||||
var difference = _.difference(previous, current);
|
||||
self.storage.updateHasBalance(current, true, function(err) {
|
||||
if (err) return next(err);
|
||||
self.storage.updateHasBalance(difference, false, next);
|
||||
});
|
||||
},
|
||||
], function(err) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, balance);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -639,7 +639,7 @@ Storage.prototype.updateBlockchainTip = function(network, hashes, cb) {
|
|||
};
|
||||
|
||||
|
||||
Storage.prototype.getBlockchainTip = function(network,cb) {
|
||||
Storage.prototype.getBlockchainTip = function(network, cb) {
|
||||
this.db.collection(collections.CACHE).findOne({
|
||||
type: 'tip',
|
||||
key: network,
|
||||
|
@ -908,6 +908,56 @@ Storage.prototype.fetchRecentAddresses = function(walletId, minUsedOn, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
Storage.prototype.updateHasBalance = function(addresses, hasBalance, cb) {
|
||||
var self = this;
|
||||
|
||||
if (!_.isArray(addresses))
|
||||
addresses = [addresses];
|
||||
|
||||
async.eachLimit(addresses, 10, function(address, next) {
|
||||
// Do not use `upsert` here.
|
||||
self.db.collection(collections.ADDRESSES).update({
|
||||
address: address,
|
||||
}, {
|
||||
$set: {
|
||||
hasBalance: hasBalance,
|
||||
},
|
||||
}, {
|
||||
w: 1,
|
||||
}, next);
|
||||
}, cb);
|
||||
};
|
||||
|
||||
Storage.prototype.fetchAddressesWithBalance = function(walletId, cb) {
|
||||
var self = this;
|
||||
|
||||
function getResult(cb) {
|
||||
self.db.collection(collections.ADDRESSES).find({
|
||||
walletId: walletId,
|
||||
hasBalance: true,
|
||||
}).toArray(function(err, result) {
|
||||
if (err) return cb(err);
|
||||
if (!_.isEmpty(result)) return cb(null, result);
|
||||
self.db.collection(collections.ADDRESSES).find({
|
||||
walletId: walletId,
|
||||
hasBalance: {
|
||||
$exists: false
|
||||
},
|
||||
}).toArray(cb);
|
||||
});
|
||||
};
|
||||
|
||||
getResult(function(err, result) {
|
||||
if (err) return cb(err);
|
||||
if (!result) return cb();
|
||||
|
||||
var addresses = _.map(result, function(address) {
|
||||
return Model.Address.fromObj(address);
|
||||
});
|
||||
return cb(null, addresses);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1112,6 +1112,7 @@ describe('Wallet service', function() {
|
|||
address.isChange.should.be.false;
|
||||
address.path.should.equal('m/2147483647/0/0');
|
||||
address.type.should.equal('P2SH');
|
||||
address.hasBalance.should.be.false;
|
||||
server.getNotifications({}, function(err, notifications) {
|
||||
should.not.exist(err);
|
||||
var notif = _.find(notifications, {
|
||||
|
@ -1175,6 +1176,7 @@ describe('Wallet service', function() {
|
|||
address.isChange.should.be.false;
|
||||
address.path.should.equal('m/0/0');
|
||||
address.type.should.equal('P2SH');
|
||||
address.hasBalance.should.be.false;
|
||||
server.getNotifications({}, function(err, notifications) {
|
||||
should.not.exist(err);
|
||||
var notif = _.find(notifications, {
|
||||
|
@ -1242,6 +1244,7 @@ describe('Wallet service', function() {
|
|||
address.isChange.should.be.false;
|
||||
address.path.should.equal('m/0/0');
|
||||
address.type.should.equal('P2PKH');
|
||||
address.hasBalance.should.be.false;
|
||||
server.getNotifications({}, function(err, notifications) {
|
||||
should.not.exist(err);
|
||||
var notif = _.find(notifications, {
|
||||
|
@ -1805,7 +1808,7 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
it('should only include addresses with balance', function(done) {
|
||||
it('should only return addresses with balance', function(done) {
|
||||
helpers.stubUtxos(server, wallet, 1, function(utxos) {
|
||||
server.createAddress({}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
|
@ -1830,6 +1833,84 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
it('should tag addresses with balance', function(done) {
|
||||
helpers.stubUtxos(server, wallet, 1, function(utxos) {
|
||||
server.createAddress({}, function(err, newAddress) {
|
||||
should.not.exist(err);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.byAddress.length.should.equal(1);
|
||||
balance.byAddress[0].address.should.equal(utxos[0].address);
|
||||
server.getMainAddresses({}, function(err, addresses) {
|
||||
should.not.exist(err);
|
||||
addresses.length.should.equal(2);
|
||||
_.find(addresses, {
|
||||
address: utxos[0].address
|
||||
}).hasBalance.should.be.true;
|
||||
_.find(addresses, {
|
||||
address: newAddress.address
|
||||
}).hasBalance.should.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should only request balance for relevant addresses', function(done) {
|
||||
var clock = sinon.useFakeTimers(Date.now(), 'Date');
|
||||
helpers.stubUtxos(server, wallet, 1, function(utxos) {
|
||||
server.storage.updateHasBalance(utxos[0].address, true, function() {
|
||||
server.createAddress({}, function(err, oldAddress) {
|
||||
should.not.exist(err);
|
||||
|
||||
clock.tick(365 * 24 * 3600 * 1000); // One year
|
||||
server.createAddress({}, function(err, newAddress) {
|
||||
should.not.exist(err);
|
||||
var getUtxosSpy = sinon.spy(blockchainExplorer, 'getUtxos');
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.byAddress.length.should.equal(1);
|
||||
balance.byAddress[0].address.should.equal(utxos[0].address);
|
||||
|
||||
getUtxosSpy.callCount.should.equal(1);
|
||||
var addressesInCall = getUtxosSpy.getCalls()[0].args[0];
|
||||
addressesInCall.length.should.equal(2);
|
||||
addressesInCall.should.not.contain(oldAddress.address);
|
||||
clock.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should request balance for previously stored addresses', function(done) {
|
||||
var clock = sinon.useFakeTimers(Date.now(), 'Date');
|
||||
helpers.stubUtxos(server, wallet, 1, function(utxos) {
|
||||
server.getMainAddresses({}, function(err, addr) {
|
||||
delete addr[0].hasBalance;
|
||||
var Collections = require('../../lib/storage').collections;
|
||||
server.storage.db.collection(Collections.ADDRESSES).update({
|
||||
address: addr[0].address
|
||||
}, addr[0], function(err) {
|
||||
should.not.exist(err);
|
||||
clock.tick(365 * 24 * 3600 * 1000); // One year
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.byAddress.length.should.equal(1);
|
||||
balance.byAddress[0].address.should.equal(utxos[0].address);
|
||||
server.getMainAddresses({}, function(err, addresses) {
|
||||
_.find(addresses, {
|
||||
address: utxos[0].address
|
||||
}).hasBalance.should.be.true;
|
||||
clock.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getFeeLevels', function() {
|
||||
|
|
Loading…
Reference in New Issue