From 2b2c94533990dcf19bf48e23577794c31e0ac9fc Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Fri, 5 Aug 2016 22:34:49 -0300 Subject: [PATCH] fix confirmations Conflicts: test/integration/server.js --- lib/server.js | 35 ++++++++++-- test/integration/helpers.js | 6 ++- test/integration/server.js | 103 ++++++++++++++++++++++++++++++++---- 3 files changed, 128 insertions(+), 16 deletions(-) diff --git a/lib/server.js b/lib/server.js index e9f0a74..9718476 100644 --- a/lib/server.js +++ b/lib/server.js @@ -2617,6 +2617,7 @@ WalletService.prototype._normalizeTxHistory = function(txs) { return { txid: tx.txid, confirmations: tx.confirmations, + blockheight: tx.blockheight, fees: parseInt((tx.fees * 1e8).toFixed(0)), time: t, inputs: inputs, @@ -2625,6 +2626,19 @@ WalletService.prototype._normalizeTxHistory = function(txs) { }); }; +var _lastKnownBlockchainHeight; +WalletService.prototype._getLastKnownBlockchainHeight = function(network, cb) { + var self = this; + + var bc = self._getBlockchainExplorer(network); + bc.getBlockchainHeight(function(err, height) { + if (!err && height > 0) { + _lastKnownBlockchainHeight = height; + } + return cb(null, _lastKnownBlockchainHeight); + }); +}; + /** * Retrieves all transactions (incoming & outgoing) * Times are in UNIX EPOCH @@ -2774,6 +2788,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) { function getNormalizedTxs(addresses, from, to, cb) { var txs, fromCache, totalItems; var useCache = addresses.length >= Defaults.HISTORY_CACHE_ADDRESS_THRESOLD; + var network = Bitcore.Address(addresses[0].address).toObject().network; async.series([ @@ -2794,10 +2809,9 @@ WalletService.prototype.getTxHistory = function(opts, cb) { if (txs) return next(); var addressStrs = _.pluck(addresses, 'address'); - var network = Bitcore.Address(addressStrs[0]).toObject().network; var bc = self._getBlockchainExplorer(network); bc.getTransactions(addressStrs, from, to, function(err, rawTxs, total) { - if (err) return cb(err); + if (err) return next(err); txs = self._normalizeTxHistory(rawTxs); totalItems = total; return next(); @@ -2815,9 +2829,24 @@ WalletService.prototype.getTxHistory = function(opts, cb) { var fwdIndex = totalItems - to; if (fwdIndex < 0) fwdIndex = 0; self.storage.storeTxHistoryCache(self.walletId, totalItems, fwdIndex, txsToCache, next); - } + }, + function(next) { + if (!txs) return next(); + + // Fix tx confirmations + self._getLastKnownBlockchainHeight(network, function(err, height) { + if (err || !height) return next(err); + _.each(txs, function(tx) { + if (tx.blockheight >= 0) { + tx.confirmations = height - tx.blockheight; + } + }); + next(); + }); + }, ], function(err) { if (err) return cb(err); + return cb(null, { items: txs, fromCache: fromCache diff --git a/test/integration/helpers.js b/test/integration/helpers.js index e533926..62d4ead 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -233,9 +233,9 @@ helpers._parseAmount = function(str) { switch (match[3]) { default: - case 'btc': + case 'btc': result.amount = Utils.strip(+match[2] * 1e8); - break; + break; case 'bit': result.amount = Utils.strip(+match[2] * 1e2); break @@ -541,6 +541,7 @@ helpers.historyCacheTest = function(items) { } }], confirmations: 1, + blockheight: 423499, time: 1424472242, blocktime: 1424472242, valueOut: 0.00031454, @@ -553,6 +554,7 @@ helpers.historyCacheTest = function(items) { var t = _.clone(template); t.txid = 'txid:' + i; t.confirmations = items - i - 1; + t.blockheight = i; t.time = t.blocktime = i; ret.unshift(t); }); diff --git a/test/integration/server.js b/test/integration/server.js index 4c78801..6b42ce4 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -4077,6 +4077,7 @@ describe('Wallet service', function() { }); it('should include the note in tx history listing', function(done) { helpers.createAddresses(server, wallet, 1, 1, function(mainAddresses, changeAddress) { + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000); server._normalizeTxHistory = sinon.stub().returnsArg(0); var txs = [{ txid: '123', @@ -4092,7 +4093,7 @@ describe('Wallet service', function() { amount: 200, }], }]; - helpers.stubHistory(txs, 100); + helpers.stubHistory(txs); server.editTxNote({ txid: '123', body: 'just some note' @@ -5898,6 +5899,7 @@ describe('Wallet service', function() { describe('#getTxHistory', function() { var server, wallet, mainAddresses, changeAddresses; beforeEach(function(done) { + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000); helpers.createAndJoinWallet(1, 1, function(s, w) { server = s; wallet = w; @@ -6239,12 +6241,16 @@ describe('Wallet service', function() { }); it('should store partial cache tx history from insight', function(done) { - var h = helpers.historyCacheTest(200); + var skip = 31; + var limit = 10; + var totalItems = 200; + var currentHeight = 1000; + + var h = helpers.historyCacheTest(totalItems); helpers.stubHistory(h); + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, currentHeight); var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache'); - var skip = 31; - var limit = 10; server.getTxHistory({ skip: skip, @@ -6259,10 +6265,13 @@ describe('Wallet service', function() { txs.length.should.equal(limit); var calls = storeTxHistoryCacheSpy.getCalls(); calls.length.should.equal(1); + + calls[0].args[1].should.equal(totalItems); // total + calls[0].args[2].should.equal(totalItems - skip - limit); // position calls[0].args[3].length.should.equal(5); // 5 txs have confirmations>= 100 // should be reversed! - calls[0].args[3][0].confirmations.should.equal(skip + limit - 1); + calls[0].args[3][0].confirmations.should.equal(currentHeight - (totalItems - (skip + limit))); calls[0].args[3][0].txid.should.equal(h[skip + limit - 1].txid); server.storage.storeTxHistoryCache.restore(); done(); @@ -6270,9 +6279,10 @@ describe('Wallet service', function() { }); - it('should not cache tx history from insight', function(done) { + it('should not cache tx history when requesting txs with low # of confirmations', function(done) { var h = helpers.historyCacheTest(200); helpers.stubHistory(h); + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000); var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache'); server.getTxHistory({ skip: 0, @@ -6289,11 +6299,15 @@ describe('Wallet service', function() { it('should store cache all tx history from insight', function(done) { - var h = helpers.historyCacheTest(200); - helpers.stubHistory(h); - var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache'); var skip = 195; var limit = 5; + var totalItems = 200; + var currentHeight = 1000; + + var h = helpers.historyCacheTest(totalItems); + helpers.stubHistory(h); + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, currentHeight); + var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache'); server.getTxHistory({ skip: skip, @@ -6306,12 +6320,78 @@ describe('Wallet service', function() { var calls = storeTxHistoryCacheSpy.getCalls(); calls.length.should.equal(1); + calls[0].args[1].should.equal(totalItems); // total + calls[0].args[2].should.equal(totalItems - skip - limit); // position calls[0].args[3].length.should.equal(5); // should be reversed! - calls[0].args[3][0].confirmations.should.equal(199); - calls[0].args[3][0].txid.should.equal(h[199].txid); + calls[0].args[3][0].confirmations.should.equal(currentHeight); + calls[0].args[3][0].txid.should.equal(h[totalItems - 1].txid); + server.storage.storeTxHistoryCache.restore(); + done(); + }); + }); + + it('should get real # of confirmations based on current block height', function(done) { + var _confirmations = Defaults.CONFIRMATIONS_TO_START_CACHING; + Defaults.CONFIRMATIONS_TO_START_CACHING = 6; + + var h = helpers.historyCacheTest(20); + helpers.stubHistory(h); + var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache'); + var skip = 0; + var limit = 20; + + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 100); + + server.getTxHistory({ + skip: skip, + limit: limit, + }, function(err, txs) { + should.not.exist(err); + should.exist(txs); + txs.length.should.equal(limit); + var calls = storeTxHistoryCacheSpy.getCalls(); + calls.length.should.equal(1); + + _.first(txs).confirmations.should.equal(81); + _.last(txs).confirmations.should.equal(100); + + server.storage.storeTxHistoryCache.restore(); + Defaults.CONFIRMATIONS_TO_START_CACHING = _confirmations; + done(); + }); + }); + + it('should get cached # of confirmations if current height unknown', function(done) { + var _confirmations = Defaults.CONFIRMATIONS_TO_START_CACHING; + Defaults.CONFIRMATIONS_TO_START_CACHING = 6; + + var h = helpers.historyCacheTest(20); + helpers.stubHistory(h); + var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache'); + var skip = 0; + var limit = 20; + + var _getLastKnownBlockchainHeight = server._getLastKnownBlockchainHeight; + server._getLastKnownBlockchainHeight = sinon.stub().callsArgWith(1, null, null); + + server.getTxHistory({ + skip: skip, + limit: limit, + }, function(err, txs) { + should.not.exist(err); + should.exist(txs); + txs.length.should.equal(limit); + var calls = storeTxHistoryCacheSpy.getCalls(); + calls.length.should.equal(1); + + _.first(txs).confirmations.should.equal(0); + _.last(txs).confirmations.should.equal(19); + server.storage.storeTxHistoryCache.restore(); + Defaults.CONFIRMATIONS_TO_START_CACHING = _confirmations; + server._getLastKnownBlockchainHeight = _getLastKnownBlockchainHeight; done(); }); }); @@ -6319,6 +6399,7 @@ describe('Wallet service', function() { describe('Downloading history', function() { var h; beforeEach(function(done) { + blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000); h = helpers.historyCacheTest(200); helpers.stubHistory(h); server.storage.clearTxHistoryCache(server.walletId, function() {