bitcoind: paginate txids in address summary
so that one request doesn't yield a 80MB response
This commit is contained in:
parent
0387c1a6e4
commit
75c43559d4
|
@ -62,6 +62,7 @@ util.inherits(Bitcoin, Service);
|
|||
|
||||
Bitcoin.dependencies = [];
|
||||
|
||||
Bitcoin.DEFAULT_MAX_TXIDS = 1000;
|
||||
Bitcoin.DEFAULT_MAX_HISTORY = 10;
|
||||
Bitcoin.DEFAULT_SHUTDOWN_TIMEOUT = 15000;
|
||||
Bitcoin.DEFAULT_ZMQ_SUBSCRIBE_PROGRESS = 0.9999;
|
||||
|
@ -89,6 +90,7 @@ Bitcoin.DEFAULT_CONFIG_SETTINGS = {
|
|||
|
||||
Bitcoin.prototype._initDefaults = function(options) {
|
||||
// limits
|
||||
this.maxTxids = options.maxTxids || Bitcoin.DEFAULT_MAX_TXIDS;
|
||||
this.maxTransactionHistory = options.maxTransactionHistory || Bitcoin.DEFAULT_MAX_HISTORY;
|
||||
this.maxAddressesQuery = options.maxAddressesQuery || Bitcoin.DEFAULT_MAX_ADDRESSES_QUERY;
|
||||
this.shutdownTimeout = options.shutdownTimeout || Bitcoin.DEFAULT_SHUTDOWN_TIMEOUT;
|
||||
|
@ -1223,10 +1225,6 @@ Bitcoin.prototype._paginateTxids = function(fullTxids, fromArg, toArg) {
|
|||
var to = parseInt(toArg);
|
||||
if (from >= 0 && to >= 0) {
|
||||
$.checkState(from < to, '"from" (' + from + ') is expected to be less than "to" (' + to + ')');
|
||||
$.checkState(
|
||||
(to - from) <= this.maxTransactionHistory,
|
||||
'"from" (' + from + ') and "to" (' + to + ') range should be less than or equal to ' + this.maxTransactionHistory
|
||||
);
|
||||
txids = fullTxids.slice(from, to);
|
||||
} else {
|
||||
txids = fullTxids;
|
||||
|
@ -1250,6 +1248,13 @@ Bitcoin.prototype.getAddressHistory = function(addressArg, options, callback) {
|
|||
var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool;
|
||||
var addressStrings = this._getAddressStrings(addresses);
|
||||
|
||||
if ((options.to - options.from) > self.maxTransactionHistory) {
|
||||
return callback(new Error(
|
||||
'"from" (' + options.from + ') and "to" (' + options.to + ') range should be less than or equal to ' +
|
||||
self.maxTransactionHistory
|
||||
));
|
||||
}
|
||||
|
||||
self.getAddressTxids(addresses, options, function(err, txids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
|
@ -1298,6 +1303,33 @@ Bitcoin.prototype.getAddressSummary = function(addressArg, options, callback) {
|
|||
var addresses = self._normalizeAddressArg(addressArg);
|
||||
var cacheKey = addresses.join('');
|
||||
|
||||
function finishWithTxids() {
|
||||
if (!options.noTxList) {
|
||||
var allTxids = mempoolTxids.reverse().concat(summaryTxids);
|
||||
var fromArg = parseInt(options.from || 0);
|
||||
var toArg = parseInt(options.to || self.maxTxids);
|
||||
|
||||
if ((toArg - fromArg) > self.maxTxids) {
|
||||
return callback(new Error(
|
||||
'"from" (' + fromArg + ') and "to" (' + toArg + ') range should be less than or equal to ' +
|
||||
self.maxTxids
|
||||
));
|
||||
}
|
||||
var paginatedTxids;
|
||||
try {
|
||||
paginatedTxids = self._paginateTxids(allTxids, fromArg, toArg);
|
||||
} catch(e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
var allSummary = _.clone(summary);
|
||||
allSummary.txids = paginatedTxids;
|
||||
callback(null, allSummary);
|
||||
} else {
|
||||
callback(null, summary);
|
||||
}
|
||||
}
|
||||
|
||||
function querySummary() {
|
||||
async.parallel([
|
||||
function getTxList(done) {
|
||||
|
@ -1340,14 +1372,7 @@ Bitcoin.prototype.getAddressSummary = function(addressArg, options, callback) {
|
|||
return callback(err);
|
||||
}
|
||||
self.summaryCache.set(cacheKey, summary);
|
||||
if (!options.noTxList) {
|
||||
var allTxids = mempoolTxids.reverse().concat(summaryTxids);
|
||||
var allSummary = _.clone(summary);
|
||||
allSummary.txids = allTxids;
|
||||
callback(null, allSummary);
|
||||
} else {
|
||||
callback(null, summary);
|
||||
}
|
||||
finishWithTxids();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -2158,13 +2158,6 @@ describe('Bitcoin Service', function() {
|
|||
var paginated = bitcoind._paginateTxids(txids, 3, 13);
|
||||
paginated.should.deep.equal([3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
});
|
||||
it('slice txids based on "from" and "to" (3 to 30)', function() {
|
||||
var bitcoind = new BitcoinService(baseConfig);
|
||||
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
(function() {
|
||||
bitcoind._paginateTxids(txids, 3, 30);
|
||||
}).should.throw(Error);
|
||||
});
|
||||
it('slice txids based on "from" and "to" (0 to 3)', function() {
|
||||
var bitcoind = new BitcoinService(baseConfig);
|
||||
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
@ -2194,6 +2187,14 @@ describe('Bitcoin Service', function() {
|
|||
|
||||
describe('#getAddressHistory', function() {
|
||||
var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX';
|
||||
it('will give error with "from" and "to" range that exceeds max size', function(done) {
|
||||
var bitcoind = new BitcoinService(baseConfig);
|
||||
bitcoind.getAddressHistory(address, {from: 0, to: 30}, function(err) {
|
||||
should.exist(err);
|
||||
err.message.match(/^\"from/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('will give an error if length of addresses is too long', function(done) {
|
||||
var addresses = [];
|
||||
for (var i = 0; i < 101; i++) {
|
||||
|
@ -2241,7 +2242,6 @@ describe('Bitcoin Service', function() {
|
|||
var txid3 = '57b7842afc97a2b46575b490839df46e9273524c6ea59ba62e1e86477cf25247';
|
||||
var memtxid1 = 'b1bfa8dbbde790cb46b9763ef3407c1a21c8264b67bfe224f462ec0e1f569e92';
|
||||
var memtxid2 = 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce';
|
||||
|
||||
it('will handle error from getAddressTxids', function(done) {
|
||||
var bitcoind = new BitcoinService(baseConfig);
|
||||
bitcoind.nodes.push({
|
||||
|
@ -2326,6 +2326,7 @@ describe('Bitcoin Service', function() {
|
|||
})
|
||||
}
|
||||
});
|
||||
sinon.spy(bitcoind, '_paginateTxids');
|
||||
bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]);
|
||||
bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, {
|
||||
received: 30 * 1e8,
|
||||
|
@ -2334,6 +2335,9 @@ describe('Bitcoin Service', function() {
|
|||
var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj';
|
||||
var options = {};
|
||||
bitcoind.getAddressSummary(address, options, function(err, summary) {
|
||||
bitcoind._paginateTxids.callCount.should.equal(1);
|
||||
bitcoind._paginateTxids.args[0][1].should.equal(0);
|
||||
bitcoind._paginateTxids.args[0][2].should.equal(1000);
|
||||
summary.appearances.should.equal(3);
|
||||
summary.totalReceived.should.equal(3000000000);
|
||||
summary.totalSpent.should.equal(1000000000);
|
||||
|
@ -2350,6 +2354,40 @@ describe('Bitcoin Service', function() {
|
|||
done();
|
||||
});
|
||||
});
|
||||
it('will give error with "from" and "to" range that exceeds max size', function(done) {
|
||||
var bitcoind = new BitcoinService(baseConfig);
|
||||
bitcoind.nodes.push({
|
||||
client: {
|
||||
getAddressMempool: sinon.stub().callsArgWith(1, null, {
|
||||
result: [
|
||||
{
|
||||
txid: memtxid1,
|
||||
satoshis: -1000000
|
||||
},
|
||||
{
|
||||
txid: memtxid2,
|
||||
satoshis: 99999
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
});
|
||||
bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]);
|
||||
bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, {
|
||||
received: 30 * 1e8,
|
||||
balance: 20 * 1e8
|
||||
});
|
||||
var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj';
|
||||
var options = {
|
||||
from: 0,
|
||||
to: 1001
|
||||
};
|
||||
bitcoind.getAddressSummary(address, options, function(err) {
|
||||
should.exist(err);
|
||||
err.message.match(/^\"from/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('will get from cache with noTxList', function(done) {
|
||||
var bitcoind = new BitcoinService(baseConfig);
|
||||
bitcoind.nodes.push({
|
||||
|
|
Loading…
Reference in New Issue