Cache address-summary
Store cache of address summary in db when address has more than MAX_TX_QUERY_LIMIT
This commit is contained in:
parent
0a3a1b5ea6
commit
3982807b32
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
function Encoding(servicePrefix) {
|
function Encoding(servicePrefix) {
|
||||||
this.servicePrefix = 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) {
|
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid, index, input, timestamp) {
|
||||||
var prefix = new Buffer('00', 'hex');
|
var buffers = [this.servicePrefix, this.addressIndex];
|
||||||
var buffers = [this.servicePrefix, prefix];
|
|
||||||
|
|
||||||
var addressSizeBuffer = new Buffer(1);
|
var addressSizeBuffer = new Buffer(1);
|
||||||
addressSizeBuffer.writeUInt8(address.length);
|
addressSizeBuffer.writeUInt8(address.length);
|
||||||
@ -58,8 +60,7 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
||||||
var prefix = new Buffer('01', 'hex');
|
var buffers = [this.servicePrefix, this.utxoIndex];
|
||||||
var buffers = [this.servicePrefix, prefix];
|
|
||||||
|
|
||||||
var addressSizeBuffer = new Buffer(1);
|
var addressSizeBuffer = new Buffer(1);
|
||||||
addressSizeBuffer.writeUInt8(address.length);
|
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;
|
module.exports = Encoding;
|
||||||
|
|
||||||
|
|||||||
@ -340,46 +340,99 @@ AddressService.prototype.getAddressSummary = function(address, options, streamer
|
|||||||
txApperances: 0,
|
txApperances: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self._streamAddressSummary(address, options, function(err, tx) {
|
var useCache = _.isUndefined(options.after);
|
||||||
|
|
||||||
|
self._loadCache(address, result, useCache, function(err, lastCachedTx) {
|
||||||
if(err)
|
if(err)
|
||||||
return log.error(err);
|
log.error(err);
|
||||||
|
|
||||||
if(tx) {
|
if(!_.isUndefined(lastCachedTx))
|
||||||
count++;
|
options.after = lastCachedTx;
|
||||||
self._aggregateAddressSummaryResult(tx, address, result, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count >= MAX_TX_QUERY_LIMIT) {//stop quering db when limit reached
|
self._streamAddressSummary(address, options, function(err, tx) {
|
||||||
options.flag_stop = true;
|
|
||||||
result.lastItem = tx.txid();
|
|
||||||
result.incomplete = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
streamer(null, tx);
|
if(err)
|
||||||
|
return log.error(err);
|
||||||
|
|
||||||
}, function(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) {
|
if (err) {
|
||||||
return callback(err);
|
return next(err);
|
||||||
|
}
|
||||||
|
if (!value) {
|
||||||
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
result.balanceSat = parseInt(result.balanceSat.toFixed());
|
var addressCache = self._encoding.decodeAddressCacheValue(value);
|
||||||
result.totalReceivedSat = parseInt(result.totalReceivedSat.toFixed());
|
//values are in satoshis
|
||||||
result.totalSentSat = parseInt(result.totalSentSat.toFixed());
|
result.balanceSat = addressCache.balance;
|
||||||
result.txApperances = parseInt(result.txApperances.toFixed());
|
result.totalReceivedSat = addressCache.received;
|
||||||
result.unconfirmedBalanceSat = parseInt(result.unconfirmedBalanceSat.toFixed());
|
result.totalSentSat = addressCache.sent;
|
||||||
result.unconfirmedTxApperances = parseInt(result.unconfirmedTxApperances.toFixed());
|
result.txApperances = addressCache.txApperances;
|
||||||
|
result.unconfirmedBalanceSat = addressCache.unconfirmedBalance;
|
||||||
|
result.unconfirmedTxApperances = addressCache.unconfirmedTxApperances;
|
||||||
|
|
||||||
result.balance = Unit.fromSatoshis(result.balanceSat).toBTC();
|
next(null, addressCache.lastTx);
|
||||||
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) {
|
AddressService.prototype._setOutputResults = function(tx, address, result) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user