diff --git a/lib/blockchainmonitor.js b/lib/blockchainmonitor.js index 7649ca0..386cde0 100644 --- a/lib/blockchainmonitor.js +++ b/lib/blockchainmonitor.js @@ -223,6 +223,10 @@ BlockchainMonitor.prototype._fetchAndProcessBlock = function(network, hash, cb) }; +BlockchainMonitor.prototype._getBlockPrevHash = function(block) { + return block.header.toObject().prevHash; +}; + BlockchainMonitor.prototype._handleNewBlockchainTip = function(network, hash, cb) { var self = this; @@ -241,6 +245,7 @@ BlockchainMonitor.prototype._handleNewBlockchainTip = function(network, hash, cb } function fetchAndProcess(network, hash, next) { + self._fetchAndProcessBlock(network, hash, function(err, block) { if (err) return next(err); @@ -250,19 +255,21 @@ BlockchainMonitor.prototype._handleNewBlockchainTip = function(network, hash, cb throw ('Block not found!' + network + hash); }; - var header = block.header.toObject(); + // to make it testeable, it is on a different function + var prevHash = self._getBlockPrevHash(block); - if (_.indexOf(tip.hashes, header.prevHash) == -1) { - log.info('', 'Prevhash does not match stored tip hashes... ' + header.prevHash); - return fetchAndProcess(network, header.prevHash, next); + + if (_.indexOf(tip.hashes, prevHash) == -1) { + log.info('', 'Prevhash does not match stored tip hashes... ' + prevHash); + return fetchAndProcess(network, prevHash, next); } - - - return cb(); + return next(); }); }; fetchAndProcess(network, hash, function(err) { + if (err) return cb(err); + // Block is chained with our processed blocks tip.hashes.unshift(hash); var hashes = tip.hashes.splice(0, Defaults.MAX_REORG_DEPTH); diff --git a/lib/storage.js b/lib/storage.js index 358b71d..ad25051 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -895,13 +895,9 @@ Storage.prototype.fetchRecentAddresses = function(walletId, minUsedOn, cb) { this.db.collection(collections.ADDRESSES).find({ walletId: walletId, - $or: [{ - lastUsedOn: { - $gte: minUsedOn, - } - }, { - lastUsedOn: undefined, - }], + lastUsedOn: { + $gte: minUsedOn, + }, }).toArray(function(err, result) { if (err) return cb(err); if (!result) return cb(); diff --git a/test/integration/bcmonitor.js b/test/integration/bcmonitor.js index 30b5bf2..60629b0 100644 --- a/test/integration/bcmonitor.js +++ b/test/integration/bcmonitor.js @@ -25,7 +25,7 @@ socket.on = function(eventName, handler) { }; describe('Blockchain monitor', function() { - var server, wallet; + var server, wallet, bcmonitor; before(function(done) { helpers.before(done); @@ -43,7 +43,7 @@ describe('Blockchain monitor', function() { helpers.createAndJoinWallet(2, 3, function(s, w) { server = s; wallet = w; - var bcmonitor = new BlockchainMonitor(); + bcmonitor = new BlockchainMonitor(); bcmonitor.start({ lockOpts: {}, messageBroker: server.messageBroker, @@ -135,9 +135,7 @@ describe('Blockchain monitor', function() { it('should process incoming blocks', function(done) { - var incoming = { - hash: '123', - }; + var incoming = '1234'; blockchainExplorer.getBlock = sinon.stub().yields(null, { rawblock: TestData.block.rawblock @@ -157,36 +155,57 @@ describe('Blockchain monitor', function() { // - helpers.insertFakeAddresses(server, wallet, fakeAddresses, function(err) { + helpers.insertFakeAddresses(server, wallet, fakeAddresses, null, function(err) { should.not.exist(err); - socket.handlers['block'](incoming); - setTimeout(function() { - var clock = sinon.useFakeTimers(TestData.block.time, 'Date'); - storage.fetchRecentAddresses(wallet.id, (Date.now() / 1000) - 100, function(err, addr) { - _.pluck(addr, 'address').should.be.deep.equal(fakeAddresses); - clock.restore(); - done(); - }); - }, 50); + var clock = sinon.useFakeTimers(TestData.block.time, 'Date'); + storage.fetchRecentAddresses(wallet.id, (Date.now() / 1000) - 100, function(err, addr) { + _.pluck(addr, 'address').should.be.deep.equal([]); + clock.restore(); + + // addresses should be marked with block's timestamp + socket.handlers['block'](incoming); + + + var storeOld = bcmonitor._storeAndBroadcastNotification; + bcmonitor._storeAndBroadcastNotification = function() { + bcmonitor._storeAndBroadcastNotification = storeOld; + var clock = sinon.useFakeTimers(TestData.block.time, 'Date'); + storage.fetchRecentAddresses(wallet.id, (Date.now() / 1000) - 100, function(err, addr) { + _.pluck(addr, 'address').should.be.deep.equal(fakeAddresses); + clock.restore(); + done(); + }); + } + }); }); }); }); - // TODO it('should process all blocks until the tip is found', function(done) { - var incoming = { - hash: '123', - }; + var incoming = '1234'; blockchainExplorer.getBlock = sinon.stub().yields(null, { rawblock: TestData.block.rawblock }); - server.storage.getBlockchainTip = sinon.stub().yields(null); + var i = 0; + var prevOld = bcmonitor._getBlockPrevHash; + var tipHashes = [0, 0, TestData.block.prev, 0]; + var expected = _.clone(tipHashes); + expected.unshift(incoming); + + bcmonitor._getBlockPrevHash = function() { + return (i++ > 5) ? TestData.block.prev : '00'; + }; + server.storage.getBlockchainTip = sinon.stub().yields(null, { + hashes: tipHashes + }); + + var spy = sinon.spy(server.storage, 'updateBlockchainTip'); var fakeAddresses = TestData.block.addresses.splice(0, 3); @@ -194,11 +213,17 @@ describe('Blockchain monitor', function() { should.not.exist(err); var aLongTimeAgo = Date.now() - (1000 * 10 * 86400); - socket.handlers['block'](incoming); - setTimeout(function() { + + + var storeOld = bcmonitor._storeAndBroadcastNotification; + bcmonitor._storeAndBroadcastNotification = function() { + bcmonitor._getBlockPrevHash = prevOld; + bcmonitor._storeAndBroadcastNotification = storeOld; + + spy.getCall(0).args[1].should.deep.equal(expected); done(); - }, 50); + }; }); }); diff --git a/test/integration/helpers.js b/test/integration/helpers.js index 8060ad3..09a3efc 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -419,12 +419,13 @@ helpers.createAddresses = function(server, wallet, main, change, cb) { }; -helpers.insertFakeAddresses = function(server, wallet, addresses, cb) { +helpers.insertFakeAddresses = function(server, wallet, addresses, lastUsedOn, cb) { async.each(addresses, function(addr, i_cb) { var addressObj = Model.Address.create({ walletId: wallet.id, address: addr, }); + addressObj.lastUsedOn = _.isUndefined(lastUsedOn) ? Math.floor(Date.now()/1000) : lastUsedOn; server.storage.storeAddressAndWallet(wallet, addressObj, function(err) { return i_cb(err); });