Browse Source

2step optimizations

feat/2-step-opt
Matias Alejo Garcia 7 years ago
parent
commit
85156a8898
No known key found for this signature in database GPG Key ID: 2470DB551277AB3
  1. 22
      lib/blockchainmonitor.js
  2. 3
      lib/common/defaults.js
  3. 139
      lib/server.js
  4. 156
      lib/storage.js
  5. 23
      package-lock.json
  6. 2
      package.json
  7. 107
      test/integration/bcmonitor.js
  8. 255
      test/integration/server.js

22
lib/blockchainmonitor.js

@ -152,7 +152,6 @@ BlockchainMonitor.prototype._handleThirdPartyBroadcasts = function(data, process
BlockchainMonitor.prototype._handleIncomingPayments = function(coin, network, data) {
var self = this;
if (!data || !data.vout) return;
var outs = _.compact(_.map(data.vout, function(v) {
@ -197,7 +196,7 @@ BlockchainMonitor.prototype._handleIncomingPayments = function(coin, network, da
walletId: walletId,
});
self.storage.softResetTxHistoryCache(walletId, function() {
self._updateActiveAddresses(address, function() {
self._updateAddressesWithBalance(address, function() {
self._storeAndBroadcastNotification(notification, next);
});
});
@ -208,14 +207,27 @@ BlockchainMonitor.prototype._handleIncomingPayments = function(coin, network, da
});
};
BlockchainMonitor.prototype._updateActiveAddresses = function(address, cb) {
BlockchainMonitor.prototype._updateAddressesWithBalance = function(address, cb) {
var self = this;
self.storage.storeActiveAddresses(address.walletId, address.address, function(err) {
self.storage.fetchAddressesWithBalance(address.walletId, function(err, result) {
if (err) {
log.warn('Could not update wallet cache', err);
return cb(err);
}
return cb(err);
var addresses = _.map(result,'address');
if (_.indexOf(addresses, address.address) >= 0) {
return cb();
}
addresses.push(address.address);
self.storage.storeAddressesWithBalance(address.walletId, addresses, function(err) {
if (err) {
log.warn('Could not update wallet cache', err);
}
return cb(err);
});
});
};

3
lib/common/defaults.js

@ -63,6 +63,9 @@ Defaults.TWO_STEP_BALANCE_THRESHOLD = 100;
// Age Limit for addresses to be considered 'active' always
Defaults.TWO_STEP_CREATION_HOURS = 24;
// Time to prevent re-quering inactive addresses (MIN)
Defaults.TWO_STEP_INACTIVE_CLEAN_DURATION_MIN = 60;
Defaults.FIAT_RATE_PROVIDER = 'BitPay';
Defaults.FIAT_RATE_FETCH_INTERVAL = 10; // In minutes
Defaults.FIAT_RATE_MAX_LOOK_BACK_TIME = 120; // In minutes

139
lib/server.js

@ -1141,7 +1141,6 @@ WalletService.prototype._getUtxosForCurrentWallet = function(opts, cb) {
next();
},
function(next) {
self._getUtxos(coin, addressStrs, function(err, utxos) {
if (err) return next(err);
@ -1287,16 +1286,8 @@ WalletService.prototype._getBalanceOneStep = function(opts, cb) {
if (err) return cb(err);
// Update cache
async.series([
function(next) {
self.storage.cleanActiveAddresses(self.walletId, next);
},
function(next) {
var active = _.map(balance.byAddress, 'address')
self.storage.storeActiveAddresses(self.walletId, active, next);
},
], function(err) {
var withBalance = _.map(balance.byAddress, 'address')
self.storage.storeAddressesWithBalance(self.walletId, withBalance, function(err) {
if (err) {
log.warn('Could not update wallet cache', err);
}
@ -1310,29 +1301,47 @@ WalletService.prototype._getBalanceOneStep = function(opts, cb) {
WalletService.prototype._getActiveAddresses = function(cb) {
var self = this;
self.storage.fetchActiveAddresses(self.walletId, function(err, active) {
self.storage.fetchAddressesWithBalance(self.walletId, function(err, addressesWB) {
if (err) {
log.warn('Could not fetch active addresses from cache', err);
return cb();
}
if (!_.isArray(addressesWB))
addressesWB = [];
if (!_.isArray(active)) return cb();
var now = Math.floor(Date.now() / 1000);
var fromTs = now - Defaults.TWO_STEP_CREATION_HOURS * 3600;
self.storage.fetchAddresses(self.walletId, function(err, allAddresses) {
self.storage.fetchNewAddresses(self.walletId, fromTs, function(err, recent) {
if (err) return cb(err);
var now = Math.floor(Date.now() / 1000);
var recent = _.map(_.filter(allAddresses, function(address) {
return address.createdOn > (now - Defaults.TWO_STEP_CREATION_HOURS * 3600);
}), 'address');
var result = _.uniq(_.union(addressesWB, recent), 'address');
return cb(null, result);
});
});
};
var result = _.union(active, recent);
WalletService.prototype._checkAndUpdateAddressCount = function(twoStepCache, cb) {
var self = this;
var index = _.indexBy(allAddresses, 'address');
result = _.compact(_.map(result, function(r) {
return index[r];
}));
return cb(null, result);
if (twoStepCache.addressCount > Defaults.TWO_STEP_BALANCE_THRESHOLD) {
log.info('Not counting addresses for '+ self.walletId);
return cb(null, true);
}
self.storage.countAddresses(self.walletId, function(err, addressCount) {
if (err) return cb(err);
if (addressCount < Defaults.TWO_STEP_BALANCE_THRESHOLD)
return cb(null, false);
twoStepCache.addressCount = addressCount;
// updates cache
self.storage.storeTwoStepCache(self.walletId, twoStepCache, function(err) {
if (err) return cb(err);
return cb(null, true);
});
});
};
@ -1354,40 +1363,66 @@ WalletService.prototype.getBalance = function(opts, cb) {
return cb(new ClientError('Invalid coin'));
}
if (!opts.twoStep)
if (!opts.twoStep) {
return self._getBalanceOneStep(opts, cb);
}
self.storage.countAddresses(self.walletId, function(err, nbAddresses) {
self.storage.getTwoStepCache(self.walletId, function(err, twoStepCache) {
if (err) return cb(err);
if (nbAddresses < Defaults.TWO_STEP_BALANCE_THRESHOLD) {
return self._getBalanceOneStep(opts, cb);
}
self._getActiveAddresses(function(err, activeAddresses) {
twoStepCache = twoStepCache || {};
self._checkAndUpdateAddressCount(twoStepCache, function(err, needsTwoStep ) {
if (err) return cb(err);
if (!_.isArray(activeAddresses)) {
if (!needsTwoStep) {
return self._getBalanceOneStep(opts, cb);
} else {
log.debug('Requesting partial balance for ' + activeAddresses.length + ' out of ' + nbAddresses + ' addresses');
self._getBalanceFromAddresses({
coin: opts.coin,
addresses: activeAddresses
}, function(err, partialBalance) {
if (err) return cb(err);
cb(null, partialBalance);
setTimeout(function() {
self._getBalanceOneStep(opts, function(err, fullBalance) {
if (err) return;
if (!_.isEqual(partialBalance, fullBalance)) {
log.info('Balance in active addresses differs from final balance');
self._notify('BalanceUpdated', fullBalance, {
isGlobal: true
});
}
});
}, 1);
return;
});
}
self._getActiveAddresses(function(err, activeAddresses) {
if (err) return cb(err);
if (!_.isArray(activeAddresses)) {
return self._getBalanceOneStep(opts, cb);
} else {
log.debug('Requesting partial balance for ' + activeAddresses.length + ' addresses');
self._getBalanceFromAddresses({
coin: opts.coin,
addresses: activeAddresses
}, function(err, partialBalance) {
if (err) return cb(err);
cb(null, partialBalance);
var now = Math.floor(Date.now() / 1000);
if (twoStepCache.lastEmpty > now - Defaults.TWO_STEP_INACTIVE_CLEAN_DURATION_MIN * 60 ) {
return;
}
setTimeout(function() {
self._getBalanceOneStep(opts, function(err, fullBalance) {
if (err) return;
if (!_.isEqual(partialBalance, fullBalance)) {
log.info('Balance in active addresses differs from final balance');
self._notify('BalanceUpdated', fullBalance, {
isGlobal: true
});
} else {
// updates cache
twoStepCache.lastEmpty = now;
// updates cache
return self.storage.storeTwoStepCache(self.walletId, twoStepCache, function(err) {
return;
});
}
});
}, 1);
return;
});
}
});
});
});
};

156
lib/storage.js

@ -64,6 +64,10 @@ Storage.prototype._createIndexes = function() {
this.db.collection(collections.ADDRESSES).createIndex({
address: 1,
});
this.db.collection(collections.ADDRESSES).createIndex({
walletId: 1,
address: 1,
});
this.db.collection(collections.EMAIL_QUEUE).createIndex({
id: 1,
});
@ -454,6 +458,28 @@ Storage.prototype.fetchAddresses = function(walletId, cb) {
});
};
Storage.prototype.fetchNewAddresses = function(walletId, fromTs, cb) {
var self = this;
this.db.collection(collections.ADDRESSES).find({
walletId: walletId,
createdOn: {
$gte: fromTs,
},
}).sort({
createdOn: 1
}).toArray(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);
});
};
Storage.prototype.countAddresses = function(walletId, cb) {
this.db.collection(collections.ADDRESSES).find({
walletId: walletId,
@ -584,52 +610,91 @@ Storage.prototype.fetchEmailByNotification = function(notificationId, cb) {
});
};
Storage.prototype.cleanActiveAddresses = function(walletId, cb) {
Storage.prototype.storeTwoStepCache = function(walletId, cacheStatus, cb) {
var self = this;
self.db.collection(collections.CACHE).update( {
walletId: walletId,
type: 'twoStep',
key: null,
}, {
"$set":
{
addressCount: cacheStatus.addressCount,
lastEmpty: cacheStatus.lastEmpty,
}
}, {
w: 1,
upsert: true,
}, cb);
};
async.series([
Storage.prototype.getTwoStepCache = function(walletId, cb) {
var self = this;
function(next) {
self.db.collection(collections.CACHE).remove({
walletId: walletId,
type: 'activeAddresses',
}, {
w: 1
}, next);
},
function(next) {
self.db.collection(collections.CACHE).insert({
walletId: walletId,
type: 'activeAddresses',
key: null
}, {
w: 1
}, next);
},
], cb);
self.db.collection(collections.CACHE).findOne({
walletId: walletId,
type: 'twoStep',
key: null
}, function(err, result) {
if (err) return cb(err);
if (!result) return cb();
return cb(null, result);
});
};
Storage.prototype.storeActiveAddresses = function(walletId, addresses, cb) {
Storage.prototype.storeAddressesWithBalance = function(walletId, addresses, cb) {
var self = this;
if (_.isEmpty(addresses)) return cb();
async.each(addresses, function(address, next) {
var record = {
walletId: walletId,
type: 'activeAddresses',
key: address,
};
self.db.collection(collections.CACHE).update({
walletId: record.walletId,
type: record.type,
key: record.key,
}, record, {
w: 1,
upsert: true,
}, next);
if (_.isEmpty(addresses))
addresses = [];
self.db.collection(collections.CACHE).update({
walletId: walletId,
type: 'addressesWithBalance',
key: null,
}, {
"$set":
{
addresses: addresses,
}
}, {
w: 1,
upsert: true,
}, cb);
};
Storage.prototype.fetchAddressesWithBalance = function(walletId, cb) {
var self = this;
self.db.collection(collections.CACHE).findOne({
walletId: walletId,
type: 'addressesWithBalance',
key: null,
}, function(err, result) {
if (err) return cb(err);
if (_.isEmpty(result)) return cb(null, []);
self.db.collection(collections.ADDRESSES).find({
walletId: walletId,
address: { $in: result.addresses },
}).toArray(function(err, result2) {
if (err) return cb(err);
if (!result2) return cb(null, []);
var addresses = _.map(result2, function(address) {
return Model.Address.fromObj(address);
});
return cb(null, addresses);
});
});
};
// -------- --------------------------- Total
// > Time >
// ^to <= ^from
@ -680,7 +745,7 @@ Storage.prototype.getTxHistoryCache = function(walletId, from, to, cb) {
return cb();
}
var txs = _.pluck(result, 'tx');
var txs = _.map(result, 'tx');
return cb(null, txs);
});
})
@ -782,23 +847,6 @@ Storage.prototype.storeTxHistoryCache = function(walletId, totalItems, firstPosi
};
Storage.prototype.fetchActiveAddresses = function(walletId, cb) {
var self = this;
self.db.collection(collections.CACHE).find({
walletId: walletId,
type: 'activeAddresses',
}).toArray(function(err, result) {
if (err) return cb(err);
if (_.isEmpty(result)) return cb();
return cb(null, _.compact(_.pluck(result, 'key')));
});
};
Storage.prototype.storeFiatRate = function(providerName, rates, cb) {
var self = this;

23
package-lock.json

@ -2709,9 +2709,9 @@
}
},
"safe": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/safe/-/safe-0.4.5.tgz",
"integrity": "sha1-MWEo64HpZFEe3OKAd55aP/N/LVg=",
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/safe/-/safe-0.3.9.tgz",
"integrity": "sha1-F4FZvuRXkawhYoruLau3SlLV0HI=",
"dev": true
},
"safe-buffer": {
@ -3167,15 +3167,14 @@
}
},
"tingodb": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/tingodb/-/tingodb-0.3.5.tgz",
"integrity": "sha1-xs0G2WxzuCp4GyjDCC/EoZBaRWg=",
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/tingodb/-/tingodb-0.5.1.tgz",
"integrity": "sha1-U8rlLLTUMQgImMwZ/F0EMzoDAck=",
"dev": true,
"requires": {
"async": "0.9.2",
"bson": "0.2.22",
"lodash": "2.4.2",
"safe": "0.4.5"
"lodash": "4.11.2",
"safe": "0.3.9"
},
"dependencies": {
"bson": {
@ -3189,9 +3188,9 @@
}
},
"lodash": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
"integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
"version": "4.11.2",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.11.2.tgz",
"integrity": "sha1-1rQzixEKWOIdrlzrz9u/0rxM2zs=",
"dev": true
},
"nan": {

2
package.json

@ -61,7 +61,7 @@
"proxyquire": "^1.7.2",
"sinon": "1.10.3",
"supertest": "*",
"tingodb": "^0.3.4"
"tingodb": "^0.5.1"
},
"scripts": {
"start": "./start.sh",

107
test/integration/bcmonitor.js

@ -13,7 +13,6 @@ log.level = 'info';
var WalletService = require('../../lib/server');
var BlockchainMonitor = require('../../lib/blockchainmonitor');
var TestData = require('../testdata');
var helpers = require('./helpers');
var storage, blockchainExplorer;
@ -90,6 +89,112 @@ describe('Blockchain monitor', function() {
});
});
it('should update addressWithBalance cache on 1 incoming tx', function(done) {
server.createAddress({}, function(err, address) {
should.not.exist(err);
var incoming = {
txid: '123',
vout: [{}],
};
server.storage.fetchAddressesWithBalance(wallet.id, function(err,ret) {
should.not.exist(err);
_.isEmpty(ret).should.equal(true);
incoming.vout[0][address.address] = 1500;
socket.handlers['tx'](incoming);
setTimeout(function() {
server.storage.fetchAddressesWithBalance(wallet.id, function(err,ret) {
should.not.exist(err);
ret.length.should.equal(1);
ret[0].address.should.equal(address.address);
done();
});
}, 100);
});
});
});
it('should update addressWithBalance cache on 2 incoming tx, same address', function(done) {
server.createAddress({}, function(err, address) {
should.not.exist(err);
server.storage.fetchAddressesWithBalance(wallet.id, function(err,ret) {
should.not.exist(err);
_.isEmpty(ret).should.equal(true);
var incoming = {
txid: '123',
vout: [{}],
};
incoming.vout[0][address.address] = 1500;
socket.handlers['tx'](incoming);
setTimeout(function() {
var incoming2 = {
txid: '456',
vout: [{}],
};
incoming2.vout[0][address.address] = 2500;
socket.handlers['tx'](incoming2);
setTimeout(function() {
server.storage.fetchAddressesWithBalance(wallet.id, function(err,ret) {
should.not.exist(err);
ret.length.should.equal(1);
ret[0].address.should.equal(address.address);
done();
});
}, 100);
}, 100);
});
});
});
it('should update addressWithBalance cache on 2 incoming tx, different address', function(done) {
server.createAddress({}, function(err, address) {
should.not.exist(err);
server.createAddress({}, function(err, address2) {
should.not.exist(err);
server.storage.fetchAddressesWithBalance(wallet.id, function(err,ret) {
should.not.exist(err);
_.isEmpty(ret).should.equal(true);
var incoming = {
txid: '123',
vout: [{}],
};
incoming.vout[0][address.address] = 1500;
socket.handlers['tx'](incoming);
setTimeout(function() {
var incoming2 = {
txid: '456',
vout: [{}],
};
incoming2.vout[0][address2.address] = 500;
socket.handlers['tx'](incoming2);
setTimeout(function() {
server.storage.fetchAddressesWithBalance(wallet.id, function(err,ret) {
should.not.exist(err);
ret.length.should.equal(2);
ret[0].address.should.equal(address.address);
done();
});
}, 100);
}, 100);
});
});
});
});
it('should not notify copayers of incoming txs more than once', function(done) {
server.createAddress({}, function(err, address) {
should.not.exist(err);

255
test/integration/server.js

@ -1973,7 +1973,7 @@ describe('Wallet service', function() {
});
});
describe.only('#getBalance 2 steps', function() {
describe('#getBalance 2 steps', function() {
var server, wallet, clock;
var _threshold = Defaults.TWO_STEP_BALANCE_THRESHOLD;
beforeEach(function(done) {
@ -2032,7 +2032,8 @@ describe('Wallet service', function() {
newAddrs = addrs;
server._getActiveAddresses(function(err, active) {
should.not.exist(err);
should.not.exist(active);
active.length.should.equal(2);
helpers.stubUtxos(server, wallet, [1, 2], {
addresses: [oldAddrs[0], newAddrs[0]],
}, function() {
@ -2045,9 +2046,12 @@ describe('Wallet service', function() {
server.getBalance({
twoStep: true
}, function(err, balance) {
console.log('[server.js.2048:err:]',err); //TODO
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3));
// Only should see newAddr[0]
balance.totalAmount.should.equal(helpers.toSatoshi(2));
next();
});
},
@ -2058,6 +2062,7 @@ describe('Wallet service', function() {
server._getActiveAddresses(function(err, active) {
should.not.exist(err);
should.exist(active);
// 1 old (with balance) + 2 news
active.length.should.equal(3);
next();
});
@ -2099,6 +2104,76 @@ describe('Wallet service', function() {
});
});
it('should not count addresses if wallet have already passed the threshold', function(done) {
var oldAddrs, newAddrs, spy;
async.series([
function(next) {
spy = sinon.spy(server.storage, 'countAddresses');
helpers.createAddresses(server, wallet, 2, 0, function(addrs) {
oldAddrs = addrs;
next();
});
},
function(next) {
clock.tick(7 * Defaults.TWO_STEP_CREATION_HOURS * 3600 * 1000);
helpers.createAddresses(server, wallet, 2, 0, function(addrs) {
newAddrs = addrs;
server._getActiveAddresses(function(err, active) {
should.not.exist(err);
// the new 2
active.length.should.equal(2);
helpers.stubUtxos(server, wallet, [1, 2], {
addresses: [oldAddrs[0], newAddrs[0]],
}, function() {
next();
});
});
});
},
function(next) {
server.getBalance({
twoStep: true
}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(2));
next();
});
},
function(next) {
setTimeout(next, 100);
},
function(next) {
spy.calledOnce.should.equal(true);
next();
},
function(next) {
server.getBalance({
twoStep: true
}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3));
next();
});
},
function(next) {
setTimeout(next, 100);
},
// should NOT count addresses again! (still only one call)
function(next) {
spy.calledOnce.should.equal(true);
next();
},
], function(err) {
should.not.exist(err);
done();
});
});
it('should not trigger notification when only balance of prioritary addresses is updated', function(done) {
var oldAddrs, newAddrs;
@ -2121,6 +2196,17 @@ describe('Wallet service', function() {
});
});
},
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(next) {
server.getBalance({
twoStep: true
@ -2169,16 +2255,23 @@ describe('Wallet service', function() {
});
});
it('should resolve balance of new addresses immediately', function(done) {
var addresses;
it('should not do 2 steps if called 2 times given the first one there found no funds on non-active addresses', function(done) {
var oldAddrs, newAddrs, spy;
async.series([
function(next) {
helpers.createAddresses(server, wallet, 4, 0, function(addrs) {
addresses = addrs;
spy = sinon.spy(server, '_getBalanceOneStep');
helpers.createAddresses(server, wallet, 2, 0, function(addrs) {
oldAddrs = addrs;
next();
});
},
function(next) {
clock.tick(7 * Defaults.TWO_STEP_CREATION_HOURS * 3600 * 1000);
helpers.createAddresses(server, wallet, 2, 0, function(addrs) {
newAddrs = addrs;
helpers.stubUtxos(server, wallet, [1, 2], {
addresses: _.take(addresses, 2),
addresses: newAddrs,
}, function() {
next();
});
@ -2195,13 +2288,19 @@ describe('Wallet service', function() {
});
},
function(next) {
server.createAddress({}, function(err, addr) {
helpers.stubUtxos(server, wallet, 0.5, {
addresses: addr,
keepUtxos: true,
}, function() {
next();
});
setTimeout(next, 100);
},
// Should _oneStep should be called once
function(next) {
spy.calledOnce.should.equal(true);
next();
},
function(next) {
helpers.stubUtxos(server, wallet, 0.5, {
addresses: newAddrs[0],
keepUtxos: true,
}, function() {
next();
});
},
function(next) {
@ -2217,6 +2316,12 @@ describe('Wallet service', function() {
function(next) {
setTimeout(next, 100);
},
// Should _oneStep should be called once
function(next) {
spy.calledOnce.should.equal(true);
next();
},
// Should not trigger notification either
function(next) {
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
@ -2231,13 +2336,12 @@ describe('Wallet service', function() {
});
});
it('should not perform 2 steps when nb of addresses below threshold', function(done) {
var oldAddrs, newAddrs;
Defaults.TWO_STEP_BALANCE_THRESHOLD = 5;
it('should do 2 steps if called 2 times given the first one there found funds on non-active addresses', function(done) {
var oldAddrs, newAddrs, spy, notificationCount;
async.series([
function(next) {
spy = sinon.spy(server, '_getBalanceOneStep');
helpers.createAddresses(server, wallet, 2, 0, function(addrs) {
oldAddrs = addrs;
next();
@ -2248,7 +2352,92 @@ describe('Wallet service', function() {
helpers.createAddresses(server, wallet, 2, 0, function(addrs) {
newAddrs = addrs;
helpers.stubUtxos(server, wallet, [1, 2], {
addresses: [oldAddrs[0], newAddrs[0]],
addresses: [ oldAddrs[0], newAddrs[0] ],
}, function() {
next();
});
});
},
function(next) {
server.getBalance({
twoStep: true
}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(2));
next();
});
},
function(next) {
setTimeout(next, 100);
},
// Should not trigger notification either
function(next) {
server.getNotifications({}, function(err, notifications) {
notificationCount = notifications.length;
should.not.exist(err);
var last = _.last(notifications);
last.type.should.equal('BalanceUpdated');
next();
});
},
// Should _oneStep should be called once
function(next) {
spy.calledOnce.should.equal(true);
next();
},
function(next) {
helpers.stubUtxos(server, wallet, 0.5, {
addresses: newAddrs[0],
keepUtxos: true,
}, function() {
next();
});
},
function(next) {
server.getBalance({
twoStep: true
}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3.5));
next();
});
},
function(next) {
setTimeout(next, 100);
},
// Should _oneStep should be called TWICE
function(next) {
spy.calledTwice.should.equal(true);
next();
},
// Should not trigger notification either
function(next) {
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
notifications.length.should.equal(notificationCount);
next();
});
},
], function(err) {
should.not.exist(err);
done();
});
});
it('should resolve balance of new addresses immediately', 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();
});
@ -2264,6 +2453,26 @@ describe('Wallet service', function() {
next();
});
},
function(next) {
server.createAddress({}, function(err, addr) {
helpers.stubUtxos(server, wallet, 0.5, {
addresses: addr,
keepUtxos: true,
}, function() {
next();
});
});
},
function(next) {
server.getBalance({
twoStep: true
}, function(err, balance) {
should.not.exist(err);
should.exist(balance);
balance.totalAmount.should.equal(helpers.toSatoshi(3.5));
next();
});
},
function(next) {
setTimeout(next, 100);
},
@ -2281,8 +2490,7 @@ describe('Wallet service', function() {
});
});
it.only('should not perform 2 steps after 2 steps was just triggered', function(done) {
it('should not perform 2 steps when nb of addresses below threshold', function(done) {
var oldAddrs, newAddrs;
Defaults.TWO_STEP_BALANCE_THRESHOLD = 5;
@ -2332,6 +2540,7 @@ describe('Wallet service', function() {
});
});
});
describe('#getFeeLevels', function() {

Loading…
Cancel
Save