Added a utxo cache.

This commit is contained in:
Chris Kleeschulte 2017-10-25 17:52:36 -04:00
parent 95d5441031
commit bdea9e8517
No known key found for this signature in database
GPG Key ID: 33195D27EF6BDB7F
3 changed files with 110 additions and 76 deletions

View File

@ -6,6 +6,7 @@ var async = require('async');
var TxController = require('./transactions');
var Common = require('./common');
var _ = require('lodash');
var LRU = require('lru-cache');
function AddressController(node) {
this.node = node;
@ -14,6 +15,14 @@ function AddressController(node) {
this.txController = new TxController(node);
this.common = new Common({log: this.node.log});
this._block = this.node.services.block;
this._utxoCache = new LRU({
max: 250,
maxAge: 1000 * 10
});
// limit the size of the request to about 100,000 bytes
// we'll use 34 bytes as the size of each address.
this._maxAddresses = Math.floor(100000/34);
}
AddressController.prototype.show = function(req, res) {
@ -142,73 +151,25 @@ AddressController.prototype.check = function(addresses) {
AddressController.prototype.utxo = function(req, res) {
var self = this;
var cachedUtxos = this._utxoCache.get(req.addr);
if (cachedUtxos) {
return res.jsonp(cachedUtxos);
}
this._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) {
var results;
if(err) {
return self.common.handleErrors(err, res);
} else if (!utxos.length) {
return res.jsonp([]);
results = [];
}
res.jsonp(utxos.map(self.transformUtxo.bind(self)));
results = utxos.map(self.transformUtxo.bind(self));
self._utxoCache.set(req.addr, results);
res.jsonp(results);
});
};
// this call could take a while to run depending on what addresses are used
// considering memory constraints, we will streaming out the results for addresses
// not necessarily in the order we received them
AddressController.prototype.multiutxo = function(req, res) {
var self = this;
var addresses;
if (_.isArray(req.addrs)) {
addresses = _.uniq(req.addrs);
} else {
addresses = req.addrs.split(',');
}
var addressesLeft = addresses.length;
var startedWriting = false;
res.write('[');
var sep = ',';
async.eachLimit(addresses, 4, function(addr, next) {
self._address.getAddressUnspentOutputs(addr, {}, function(err, utxos) {
if (err) {
return next(err);
}
if (addressesLeft-- > 0 && utxos.length > 0 && startedWriting) {
res.write(sep);
}
for(var i = 0; i < utxos.length; i++) {
startedWriting = true;
if (utxos.length - 1 === i) {
sep = '';
}
res.write(JSON.stringify(self.transformUtxo(utxos[i])) + sep);
}
sep = ',';
next();
});
}, function(err) {
if (err) {
return self.common.handleErrors(err, res);
}
res.write(']');
res.end();
});
};
AddressController.prototype.transformUtxo = function(utxoArg) {
var utxo = {
address: utxoArg.address,
@ -238,6 +199,81 @@ AddressController.prototype._getTransformOptions = function(req) {
};
};
// this call could take a while to run depending on what addresses are used
// considering memory constraints, we will streaming out the results for addresses
// not necessarily in the order we received them
AddressController.prototype.multiutxo = function(req, res) {
var self = this;
var addresses;
if (_.isArray(req.addrs)) {
addresses = _.uniq(req.addrs);
} else {
addresses = _.compact(req.addrs.split(','));
}
var cacheKey = addresses.join('');
if (addresses.length > this._maxAddresses) {
return self.common.handleErrors(new Error('Too many addresses.'), res);
}
var cachedUtxos = this._utxoCache.get(cacheKey);
if (cachedUtxos) {
return res.jsonp(cachedUtxos);
}
var addressesLeft = addresses.length;
var startedWriting = false;
var cache = [];
res.write('[');
var sep = ',';
async.eachLimit(addresses, 4, function(addr, next) {
self._address.getAddressUnspentOutputs(addr, {}, function(err, utxos) {
if (err) {
return next(err);
}
if (addressesLeft-- > 0 && utxos.length > 0 && startedWriting) {
res.write(sep);
}
for(var i = 0; i < utxos.length; i++) {
startedWriting = true;
if (utxos.length - 1 === i) {
sep = '';
}
cache.push(utxos[i]);
res.write(JSON.stringify(self.transformUtxo(utxos[i])) + sep);
}
sep = ',';
next();
});
}, function(err) {
if (err) {
return self.common.handleErrors(err, res);
}
self._utxoCache.set(cacheKey, cache);
res.write(']');
res.end();
});
};
AddressController.prototype.multitxs = function(req, res) {
var self = this;

30
package-lock.json generated
View File

@ -1063,24 +1063,12 @@
"integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4="
},
"lru-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz",
"integrity": "sha1-E0OVXtry432bnn7nJB4nxLn7cr4=",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
"requires": {
"pseudomap": "1.0.2",
"yallist": "2.0.0"
},
"dependencies": {
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"yallist": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz",
"integrity": "sha1-MGxUODXwnuGkyyO3vOmrNByRzdQ="
}
"yallist": "2.1.2"
}
},
"mime-db": {
@ -1542,6 +1530,11 @@
}
}
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"pump": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz",
@ -2417,6 +2410,11 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",

View File

@ -36,7 +36,7 @@
"body-parser": "^1.13.3",
"compression": "^1.6.1",
"lodash": "^2.4.1",
"lru-cache": "^4.0.1",
"lru-cache": "^4.1.1",
"morgan": "^1.7.0",
"request": "^2.64.0"
},