From 3982807b322a431b7dc931af0176f0463588881e Mon Sep 17 00:00:00 2001 From: sairajzero Date: Mon, 17 Apr 2023 20:13:29 +0530 Subject: [PATCH] Cache address-summary Store cache of address summary in db when address has more than MAX_TX_QUERY_LIMIT --- lib/services/address/encoding.js | 64 +++++++++++++++-- lib/services/address/index.js | 115 ++++++++++++++++++++++--------- 2 files changed, 144 insertions(+), 35 deletions(-) diff --git a/lib/services/address/encoding.js b/lib/services/address/encoding.js index 3f13ac1d..3c8191e6 100644 --- a/lib/services/address/encoding.js +++ b/lib/services/address/encoding.js @@ -2,11 +2,13 @@ function Encoding(servicePrefix) { this.servicePrefix = servicePrefix; + this.addressIndex = new Buffer('00', 'hex'); + this.utxoIndex = new Buffer('01', 'hex'); + this.addressCache = new Buffer('ff', 'hex'); } Encoding.prototype.encodeAddressIndexKey = function(address, height, txid, index, input, timestamp) { - var prefix = new Buffer('00', 'hex'); - var buffers = [this.servicePrefix, prefix]; + var buffers = [this.servicePrefix, this.addressIndex]; var addressSizeBuffer = new Buffer(1); addressSizeBuffer.writeUInt8(address.length); @@ -58,8 +60,7 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) { }; Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) { - var prefix = new Buffer('01', 'hex'); - var buffers = [this.servicePrefix, prefix]; + var buffers = [this.servicePrefix, this.utxoIndex]; var addressSizeBuffer = new Buffer(1); addressSizeBuffer.writeUInt8(address.length); @@ -114,5 +115,60 @@ Encoding.prototype.decodeUtxoIndexValue = function(buffer) { }; }; +Encoding.prototype.encodeAddressCacheKey = function(address) { + return Buffer.concat([this.servicePrefix, this.addressCache, new Buffer(address, 'utf8')]); +} + +Encoding.prototype.decodeAddressCacheKey = function(buffer) { + return buffer.slice(3).toString('utf8'); +} + +Encoding.prototype.encodeAddressCacheValue = function(lastTx, balance, received, sent, txApperances, unconfirmedBalance, unconfirmedTxApperances) { + + var buffer = []; + + var balanceBuffer = new Buffer(4); + balanceBuffer.writeUInt32BE(balance || 0); + buffer.push(balanceBuffer); + + var receivedBuffer = new Buffer(4); + receivedBuffer.writeUInt32BE(received || 0); + buffer.push(receivedBuffer); + + var sentBuffer = new Buffer(4); + sentBuffer.writeUInt32BE(sent || 0); + buffer.push(sentBuffer); + + var txApperancesBuffer = new Buffer(4); + txApperancesBuffer.writeUInt32BE(txApperances || 0); + buffer.push(txApperancesBuffer); + + var unconfirmedBalanceBuffer = new Buffer(4); + unconfirmedBalanceBuffer.writeUInt32BE(unconfirmedBalance || 0); + buffer.push(unconfirmedBalanceBuffer); + + var unconfirmedTxApperancesBuffer = new Buffer(4); + unconfirmedTxApperancesBuffer.writeUInt32BE(unconfirmedTxApperances || 0); + buffer.push(unconfirmedTxApperancesBuffer); + + var txidBuffer = new Buffer(lastTx || Array(65).join('0'), 'hex'); + buffer.push(txidBuffer); + + return Buffer.concat(buffers); +} + +Encoding.prototype.decodeAddressCacheValue = function(buffer) { + + var balance = buffer.readUInt32BE(0); + var received = buffer.readUInt32BE(4); + var sent = buffer.readUInt32BE(8); + var txApperances = buffer.readUInt32BE(12); + var unconfirmedBalance = buffer.readUInt32BE(16); + var unconfirmedTxApperances = buffer.readUInt32BE(20); + var lastTx = buffer.slice(24).toString('hex'); + + return { lastTx, balance, received, sent, txApperances, unconfirmedBalance, unconfirmedTxApperances }; +} + module.exports = Encoding; diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 57cc0deb..e0087f09 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -340,46 +340,99 @@ AddressService.prototype.getAddressSummary = function(address, options, streamer txApperances: 0, }; - self._streamAddressSummary(address, options, function(err, tx) { + var useCache = _.isUndefined(options.after); + self._loadCache(address, result, useCache, function(err, lastCachedTx) { if(err) - return log.error(err); - - if(tx) { - count++; - self._aggregateAddressSummaryResult(tx, address, result, options); - } - - if(count >= MAX_TX_QUERY_LIMIT) {//stop quering db when limit reached - options.flag_stop = true; - result.lastItem = tx.txid(); - result.incomplete = true; - } + log.error(err); - streamer(null, tx); + if(!_.isUndefined(lastCachedTx)) + options.after = lastCachedTx; - }, function(err) { + self._streamAddressSummary(address, options, function(err, tx) { + + if(err) + return log.error(err); + + if(tx) { + count++; + self._aggregateAddressSummaryResult(tx, address, result, options); + } + + if(count >= MAX_TX_QUERY_LIMIT) {//stop quering db when limit reached + options.flag_stop = true; + result.lastItem = tx.txid(); + result.incomplete = true; + } + + streamer(null, tx); + + }, function(err) { + + if (err) { + return callback(err); + } + + result.balanceSat = parseInt(result.balanceSat.toFixed()); + result.totalReceivedSat = parseInt(result.totalReceivedSat.toFixed()); + result.totalSentSat = parseInt(result.totalSentSat.toFixed()); + result.txApperances = parseInt(result.txApperances.toFixed()); + result.unconfirmedBalanceSat = parseInt(result.unconfirmedBalanceSat.toFixed()); + result.unconfirmedTxApperances = parseInt(result.unconfirmedTxApperances.toFixed()); + + result.balance = Unit.fromSatoshis(result.balanceSat).toBTC(); + result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC(); + result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC(); + result.unconfirmedBalance = Unit.fromSatoshis(result.unconfirmedBalanceSat).toBTC(); + + callback(null, result); + + //store in cache if needed + if(useCache && result.incomplete) + this._storeCache(address, result.lastItem, result); + + }); + + }) + +} + +AddressService.prototype._storeCache = function(address, lastCacheTx, result, callback) { + var key = self._encoding.encodeAddressCacheKey(address); + var value = self._encoding.encodeAddressCacheValue(lastCacheTx, result.balanceSat, result.totalReceivedSat, result.totalSentSat, result.txApperances, result.unconfirmedBalanceSat, result.unconfirmedTxApperances) + + if(!_.isFunction(callback)) //if callback is not passed, call a empty function + callback = () => null; + + self._db.put(key, value, callback); +} + +AddressService.prototype._loadCache = function(address, result, useCache, next) { + + if(!useCache) //skip if useCache is false (cases like 'after' parameter is used by client) + next(); + + self._db.get(self._encoding.encodeAddressCacheKey(address), function(err, value) { if (err) { - return callback(err); + return next(err); } + if (!value) { + return next(); + } + + var addressCache = self._encoding.decodeAddressCacheValue(value); + //values are in satoshis + result.balanceSat = addressCache.balance; + result.totalReceivedSat = addressCache.received; + result.totalSentSat = addressCache.sent; + result.txApperances = addressCache.txApperances; + result.unconfirmedBalanceSat = addressCache.unconfirmedBalance; + result.unconfirmedTxApperances = addressCache.unconfirmedTxApperances; + + next(null, addressCache.lastTx); - result.balanceSat = parseInt(result.balanceSat.toFixed()); - result.totalReceivedSat = parseInt(result.totalReceivedSat.toFixed()); - result.totalSentSat = parseInt(result.totalSentSat.toFixed()); - result.txApperances = parseInt(result.txApperances.toFixed()); - result.unconfirmedBalanceSat = parseInt(result.unconfirmedBalanceSat.toFixed()); - result.unconfirmedTxApperances = parseInt(result.unconfirmedTxApperances.toFixed()); - - result.balance = Unit.fromSatoshis(result.balanceSat).toBTC(); - result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC(); - result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC(); - result.unconfirmedBalance = Unit.fromSatoshis(result.unconfirmedBalanceSat).toBTC(); - - callback(null, result); - }); - } AddressService.prototype._setOutputResults = function(tx, address, result) {