From 3fbcbbe7bcc2b793bd435257cdb153563bd55ded Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 28 Jan 2023 20:18:23 +0530 Subject: [PATCH] Fixed: APIs giving incorrect data - Fixed: addr API giving decimals in satoshi values - Fixed: Incorrect balance, totalSent, totalReceived values returned in API calls (issue: duplication) - Fixed: incorrect totalCount value in addr API and duplication of tx list --- lib/services/address/index.js | 151 ++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 18 deletions(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 8028da06..ce16a1f0 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -180,7 +180,7 @@ AddressService.prototype.getAddressHistory = function(addresses, options, stream options.from = options.from || 0; options.to = options.to || 0xffffffff; - if(!callback){ //if only 3 args, then streamer is callback + if(typeof callback !== 'function'){ //if only 3 args, then streamer is callback callback = streamer; streamer = () => null; //NULL fn } @@ -202,13 +202,15 @@ AddressService.prototype.getAddressHistory = function(addresses, options, stream self._streamAddressSummary(address, options, function(err, tx){ - results.totalCount++; - if(err) return log.error(err); - if(!options.txNotNeeded && results.items.length < MAX_TX_QUERY_LIMIT) - results.items.push(tx); + if(!options.txNotNeeded) { + let count = self._getOccurrenceCount(tx, address); + results.totalCount += 1 / count; //fix for duplication + if(results.items.length < MAX_TX_QUERY_LIMIT && !results.items.some(x => x.txid() === tx.txid())) //push only if tx not already in array + results.items.push(tx); + } streamer(null, tx); @@ -220,7 +222,10 @@ AddressService.prototype.getAddressHistory = function(addresses, options, stream return callback(err); } - //TODO: sorting of tx list (results.items) + //sort items in desc block-height, then asc txid (if same height) + results.items.sort((a, b) => b.__height - a.__height || a.txid().localeCompare(b.txid())); + results.totalCount = parseInt(results.totalCount.toFixed()); + callback(null, results); }) @@ -285,7 +290,7 @@ AddressService.prototype.getAddressSummary = function(address, options, streamer options.queryMempool = true; } - if(!callback){ //if only 3 args, then streamer is callback + if(typeof callback !== 'function'){ //if only 3 args, then streamer is callback callback = streamer; streamer = () => null; //NULL fn } @@ -320,6 +325,11 @@ AddressService.prototype.getAddressSummary = function(address, options, streamer 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.balance = Unit.fromSatoshis(result.balanceSat).toBTC(); result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC(); result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC(); @@ -373,10 +383,13 @@ AddressService.prototype._setInputResults = function(tx, address, result) { } }; -AddressService.prototype._aggregateAddressSummaryResult = function (tx, address, result, options){ - +AddressService.prototype._getAddressSummaryResult = function(txs, address, result, options) { + var self = this; + for(var i = 0; i < txs.length; i++) { + var tx = txs[i]; + self._setOutputResults(tx, address, result); self._setInputResults(tx, address, result); @@ -387,20 +400,122 @@ AddressService.prototype._aggregateAddressSummaryResult = function (tx, address, result.transactions.push(tx.txid()); } -} - -AddressService.prototype._getAddressSummaryResult = function(txs, address, result, options) { - - var self = this; - - for(var i = 0; i < txs.length; i++) { - var tx = txs[i]; - self._aggregateAddressSummaryResult(tx, address, result, options); } return result; }; +AddressService.prototype._getOccurrenceCount = function(tx, address) { + let count = 0; + + for(var i = 0; i < tx.inputs.length; i++) { + + var input = tx.inputs[i]; + + if(utils.getAddress(input, this._network) === address) + count++; + + } + + for(var j = 0; j < tx.outputs.length; j++) { + + var output = tx.outputs[j]; + + if(utils.getAddress(output, this._network) === address) + count++; + + } + + return count; + +} + +AddressService.prototype._getOutputResults = function(tx, address) { + + let result = { value: 0, count:0 }; + + for(var j = 0; j < tx.outputs.length; j++) { + + var output = tx.outputs[j]; + + if (utils.getAddress(output, this._network) !== address) { + continue; + } + + result.value += output.value; + result.count++; + + } + + return result; + +}; + +AddressService.prototype._getInputResults = function(tx, address) { + + let result = { value: 0, count:0 }; + + for(var i = 0; i < tx.inputs.length; i++) { + + var input = tx.inputs[i]; + + if (utils.getAddress(input, this._network) !== address) { + continue; + } + + result.value += tx.__inputValues[i]; + result.count++; + + } + + return result; + +}; + +AddressService.prototype._aggregateAddressSummaryResult = function (tx, address, result, options){ + + var self = this; + + let output = self._getOutputResults(tx, address); + let input = self._getInputResults(tx, address); + + //Since tx with multiple (x) input/output occurances of address will invoke this fn x time(s), (as we are not storing txid and hence cannot check for duplications) + //we divide the values by x and aggregate it to result. + //eg. tx with 1 input, 1 output => x=1+1=2.... input_val = 2, output_val = 1. + //the values will be aggregated 2 times, hence, we divide values by x i.e, 2. + //now agg_input_val = 2/2 =1, agg_output_val = 1/2 =0.5 + //the fn ll be called x times, hence the total result will be, result=agg*x: input(1*2=2), output(0.5*2=1) + + let total_count = input.count + output.count; + let div_input_val = input.value / total_count, + div_output_val = output.value / total_count; + + //aggregate the result + result.txApperances += 1/total_count; + + result.totalReceivedSat += div_output_val; + result.balanceSat += div_output_val; + + result.totalSentSat += div_input_val; + result.balanceSat -= div_input_val; + + if(!tx.confirmations){ + result.unconfirmedTxApperances += 1/total_count; + result.unconfirmedBalanceSat += div_output_val; + result.unconfirmedBalanceSat -= div_input_val; + } + + if (!options.noTxList) { + if (!result.transactions) { + result.transactions = []; + } + let txid = tx.txid(); + if(!result.transactions.includes(txid) && result.transactions.length < MAX_TX_QUERY_LIMIT) //push txid only if its not in the array (list limit not maxed out) + result.transactions.push(txid); + } + +} + AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) { var self = this;