From bdea9e8517e3ddd214f651aa699c751729b94d54 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Wed, 25 Oct 2017 17:52:36 -0400 Subject: [PATCH] Added a utxo cache. --- lib/addresses.js | 154 ++++++++++++++++++++++++++++------------------ package-lock.json | 30 +++++---- package.json | 2 +- 3 files changed, 110 insertions(+), 76 deletions(-) diff --git a/lib/addresses.js b/lib/addresses.js index cfb3926..854a718 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -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; diff --git a/package-lock.json b/package-lock.json index 70cbd71..f49fa4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 40b6e14..66cf8fe 100644 --- a/package.json +++ b/package.json @@ -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" },