parent
8342d22041
commit
2b2c945339
|
@ -2617,6 +2617,7 @@ WalletService.prototype._normalizeTxHistory = function(txs) {
|
||||||
return {
|
return {
|
||||||
txid: tx.txid,
|
txid: tx.txid,
|
||||||
confirmations: tx.confirmations,
|
confirmations: tx.confirmations,
|
||||||
|
blockheight: tx.blockheight,
|
||||||
fees: parseInt((tx.fees * 1e8).toFixed(0)),
|
fees: parseInt((tx.fees * 1e8).toFixed(0)),
|
||||||
time: t,
|
time: t,
|
||||||
inputs: inputs,
|
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)
|
* Retrieves all transactions (incoming & outgoing)
|
||||||
* Times are in UNIX EPOCH
|
* Times are in UNIX EPOCH
|
||||||
|
@ -2774,6 +2788,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
|
||||||
function getNormalizedTxs(addresses, from, to, cb) {
|
function getNormalizedTxs(addresses, from, to, cb) {
|
||||||
var txs, fromCache, totalItems;
|
var txs, fromCache, totalItems;
|
||||||
var useCache = addresses.length >= Defaults.HISTORY_CACHE_ADDRESS_THRESOLD;
|
var useCache = addresses.length >= Defaults.HISTORY_CACHE_ADDRESS_THRESOLD;
|
||||||
|
var network = Bitcore.Address(addresses[0].address).toObject().network;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
|
|
||||||
|
@ -2794,10 +2809,9 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
|
||||||
if (txs) return next();
|
if (txs) return next();
|
||||||
|
|
||||||
var addressStrs = _.pluck(addresses, 'address');
|
var addressStrs = _.pluck(addresses, 'address');
|
||||||
var network = Bitcore.Address(addressStrs[0]).toObject().network;
|
|
||||||
var bc = self._getBlockchainExplorer(network);
|
var bc = self._getBlockchainExplorer(network);
|
||||||
bc.getTransactions(addressStrs, from, to, function(err, rawTxs, total) {
|
bc.getTransactions(addressStrs, from, to, function(err, rawTxs, total) {
|
||||||
if (err) return cb(err);
|
if (err) return next(err);
|
||||||
txs = self._normalizeTxHistory(rawTxs);
|
txs = self._normalizeTxHistory(rawTxs);
|
||||||
totalItems = total;
|
totalItems = total;
|
||||||
return next();
|
return next();
|
||||||
|
@ -2815,9 +2829,24 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
|
||||||
var fwdIndex = totalItems - to;
|
var fwdIndex = totalItems - to;
|
||||||
if (fwdIndex < 0) fwdIndex = 0;
|
if (fwdIndex < 0) fwdIndex = 0;
|
||||||
self.storage.storeTxHistoryCache(self.walletId, totalItems, fwdIndex, txsToCache, next);
|
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) {
|
], function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
return cb(null, {
|
return cb(null, {
|
||||||
items: txs,
|
items: txs,
|
||||||
fromCache: fromCache
|
fromCache: fromCache
|
||||||
|
|
|
@ -233,9 +233,9 @@ helpers._parseAmount = function(str) {
|
||||||
|
|
||||||
switch (match[3]) {
|
switch (match[3]) {
|
||||||
default:
|
default:
|
||||||
case 'btc':
|
case 'btc':
|
||||||
result.amount = Utils.strip(+match[2] * 1e8);
|
result.amount = Utils.strip(+match[2] * 1e8);
|
||||||
break;
|
break;
|
||||||
case 'bit':
|
case 'bit':
|
||||||
result.amount = Utils.strip(+match[2] * 1e2);
|
result.amount = Utils.strip(+match[2] * 1e2);
|
||||||
break
|
break
|
||||||
|
@ -541,6 +541,7 @@ helpers.historyCacheTest = function(items) {
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
confirmations: 1,
|
confirmations: 1,
|
||||||
|
blockheight: 423499,
|
||||||
time: 1424472242,
|
time: 1424472242,
|
||||||
blocktime: 1424472242,
|
blocktime: 1424472242,
|
||||||
valueOut: 0.00031454,
|
valueOut: 0.00031454,
|
||||||
|
@ -553,6 +554,7 @@ helpers.historyCacheTest = function(items) {
|
||||||
var t = _.clone(template);
|
var t = _.clone(template);
|
||||||
t.txid = 'txid:' + i;
|
t.txid = 'txid:' + i;
|
||||||
t.confirmations = items - i - 1;
|
t.confirmations = items - i - 1;
|
||||||
|
t.blockheight = i;
|
||||||
t.time = t.blocktime = i;
|
t.time = t.blocktime = i;
|
||||||
ret.unshift(t);
|
ret.unshift(t);
|
||||||
});
|
});
|
||||||
|
|
|
@ -4077,6 +4077,7 @@ describe('Wallet service', function() {
|
||||||
});
|
});
|
||||||
it('should include the note in tx history listing', function(done) {
|
it('should include the note in tx history listing', function(done) {
|
||||||
helpers.createAddresses(server, wallet, 1, 1, function(mainAddresses, changeAddress) {
|
helpers.createAddresses(server, wallet, 1, 1, function(mainAddresses, changeAddress) {
|
||||||
|
blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000);
|
||||||
server._normalizeTxHistory = sinon.stub().returnsArg(0);
|
server._normalizeTxHistory = sinon.stub().returnsArg(0);
|
||||||
var txs = [{
|
var txs = [{
|
||||||
txid: '123',
|
txid: '123',
|
||||||
|
@ -4092,7 +4093,7 @@ describe('Wallet service', function() {
|
||||||
amount: 200,
|
amount: 200,
|
||||||
}],
|
}],
|
||||||
}];
|
}];
|
||||||
helpers.stubHistory(txs, 100);
|
helpers.stubHistory(txs);
|
||||||
server.editTxNote({
|
server.editTxNote({
|
||||||
txid: '123',
|
txid: '123',
|
||||||
body: 'just some note'
|
body: 'just some note'
|
||||||
|
@ -5898,6 +5899,7 @@ describe('Wallet service', function() {
|
||||||
describe('#getTxHistory', function() {
|
describe('#getTxHistory', function() {
|
||||||
var server, wallet, mainAddresses, changeAddresses;
|
var server, wallet, mainAddresses, changeAddresses;
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
|
blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000);
|
||||||
helpers.createAndJoinWallet(1, 1, function(s, w) {
|
helpers.createAndJoinWallet(1, 1, function(s, w) {
|
||||||
server = s;
|
server = s;
|
||||||
wallet = w;
|
wallet = w;
|
||||||
|
@ -6239,12 +6241,16 @@ describe('Wallet service', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should store partial cache tx history from insight', function(done) {
|
it('should store partial cache tx history from insight', function(done) {
|
||||||
var h = helpers.historyCacheTest(200);
|
|
||||||
helpers.stubHistory(h);
|
|
||||||
var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache');
|
|
||||||
|
|
||||||
var skip = 31;
|
var skip = 31;
|
||||||
var limit = 10;
|
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');
|
||||||
|
|
||||||
|
|
||||||
server.getTxHistory({
|
server.getTxHistory({
|
||||||
skip: skip,
|
skip: skip,
|
||||||
|
@ -6259,10 +6265,13 @@ describe('Wallet service', function() {
|
||||||
txs.length.should.equal(limit);
|
txs.length.should.equal(limit);
|
||||||
var calls = storeTxHistoryCacheSpy.getCalls();
|
var calls = storeTxHistoryCacheSpy.getCalls();
|
||||||
calls.length.should.equal(1);
|
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
|
calls[0].args[3].length.should.equal(5); // 5 txs have confirmations>= 100
|
||||||
|
|
||||||
// should be reversed!
|
// 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);
|
calls[0].args[3][0].txid.should.equal(h[skip + limit - 1].txid);
|
||||||
server.storage.storeTxHistoryCache.restore();
|
server.storage.storeTxHistoryCache.restore();
|
||||||
done();
|
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);
|
var h = helpers.historyCacheTest(200);
|
||||||
helpers.stubHistory(h);
|
helpers.stubHistory(h);
|
||||||
|
blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000);
|
||||||
var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache');
|
var storeTxHistoryCacheSpy = sinon.spy(server.storage, 'storeTxHistoryCache');
|
||||||
server.getTxHistory({
|
server.getTxHistory({
|
||||||
skip: 0,
|
skip: 0,
|
||||||
|
@ -6289,11 +6299,15 @@ describe('Wallet service', function() {
|
||||||
|
|
||||||
|
|
||||||
it('should store cache all tx history from insight', function(done) {
|
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 skip = 195;
|
||||||
var limit = 5;
|
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({
|
server.getTxHistory({
|
||||||
skip: skip,
|
skip: skip,
|
||||||
|
@ -6306,19 +6320,86 @@ describe('Wallet service', function() {
|
||||||
var calls = storeTxHistoryCacheSpy.getCalls();
|
var calls = storeTxHistoryCacheSpy.getCalls();
|
||||||
calls.length.should.equal(1);
|
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);
|
calls[0].args[3].length.should.equal(5);
|
||||||
|
|
||||||
// should be reversed!
|
// should be reversed!
|
||||||
calls[0].args[3][0].confirmations.should.equal(199);
|
calls[0].args[3][0].confirmations.should.equal(currentHeight);
|
||||||
calls[0].args[3][0].txid.should.equal(h[199].txid);
|
calls[0].args[3][0].txid.should.equal(h[totalItems - 1].txid);
|
||||||
server.storage.storeTxHistoryCache.restore();
|
server.storage.storeTxHistoryCache.restore();
|
||||||
done();
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Downloading history', function() {
|
describe('Downloading history', function() {
|
||||||
var h;
|
var h;
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
|
blockchainExplorer.getBlockchainHeight = sinon.stub().callsArgWith(0, null, 1000);
|
||||||
h = helpers.historyCacheTest(200);
|
h = helpers.historyCacheTest(200);
|
||||||
helpers.stubHistory(h);
|
helpers.stubHistory(h);
|
||||||
server.storage.clearTxHistoryCache(server.walletId, function() {
|
server.storage.clearTxHistoryCache(server.walletId, function() {
|
||||||
|
|
Loading…
Reference in New Issue