Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions integration/regtest-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,9 @@ describe('Node Functionality', function() {
if (err) {
throw err;
}
results.totalReceived.should.equal(2000000000);
results.totalSpent.should.equal(1999990000);
// This address receives 10 BTC (from a 50 btc input split 10BTC/39.99999045) then sends it around
results.totalReceived.should.equal(1000000000);
results.totalSpent.should.equal(999990000);
results.balance.should.equal(10000);
results.unconfirmedBalance.should.equal(0);
results.appearances.should.equal(6);
Expand Down
111 changes: 65 additions & 46 deletions lib/services/address/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1387,11 +1387,12 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
/**
* This will give an object with:
* balance - confirmed balance
* unconfirmedBalance - unconfirmed balance
* unconfirmedReceived - unconfirmed outputs sent to this address
* unconfirmedSpent - unconfirmed outputs sent from this address
* totalReceived - satoshis received
* totalSpent - satoshis spent
* appearances - number of transactions
* unconfirmedAppearances - number of unconfirmed transactions
* appearances - number of confirmed outputs sent to this address
* unconfirmedAppearances - number of unconfirmed outputs sent to this address
* txids - list of txids (unless noTxList is set)
*
* @param {String} address
Expand All @@ -1406,80 +1407,98 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
queryMempool: true
};

var outputs;
var inputs;

async.parallel(
[
function(next) {
self.getInputs(address, opt, function(err, ins) {
inputs = ins;
next(err);
});
},
function(next) {
self.getOutputs(address, opt, function(err, outs) {
outputs = outs;
next(err);
});
}
self.getInputs.bind(self, address, opt),
self.getOutputs.bind(self, address, opt)
],
function(err) {
function(err, results) {
if(err) {
return callback(err);
}
var inputs = results[0];
var outputs = results[1];

var totalReceived = 0;
var totalSpent = 0;
var balance = 0;
var unconfirmedBalance = 0;
var unconfirmedReceived = 0;
var unconfirmedSpent = 0;
var unconfirmedBalance = 0; // TODO remove this, for backcompat with insight-API
var inputAppearanceIds = {};
var appearanceIds = {};
var unconfirmedAppearanceIds = {};
var txids = [];

for(var i = 0; i < outputs.length; i++) {
for(var j = 0, len = inputs.length; j < len; j++) {
var input = inputs[j];
inputAppearanceIds[input.txid] = true; // marked for change txs
if (input.confirmations) {
appearanceIds[input.txid] = true;
} else {
unconfirmedAppearanceIds[input.txid] = true;
}
}

for(var i = 0, len = outputs.length; i < len; i++) {
var output = outputs[i];
// Bitcoind's isSpent only works for confirmed transactions
var spentDB = self.node.services.bitcoind.isSpent(outputs[i].txid, outputs[i].outputIndex);
var spentDB = self.node.services.bitcoind.isSpent(output.txid, output.outputIndex);
var spentIndexSyncKey = self._encodeSpentIndexSyncKey(
new Buffer(outputs[i].txid, 'hex'), // TODO: get buffer directly
outputs[i].outputIndex
new Buffer(output.txid, 'hex'), // TODO: get buffer directly
output.outputIndex
);
var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey];
var isChangeTx = inputAppearanceIds[output.txid];
var confirmed = output.confirmations > 0;

txids.push(outputs[i]);
txids.push(output);

if(outputs[i].confirmations) {
totalReceived += outputs[i].satoshis;
balance += outputs[i].satoshis;
appearanceIds[outputs[i].txid] = true;
} else {
unconfirmedBalance += outputs[i].satoshis;
unconfirmedAppearanceIds[outputs[i].txid] = true;
// If this is a confirmed TX, add it to our balance & totalReceived.
if(confirmed) {
totalReceived += output.satoshis;
balance += output.satoshis;
appearanceIds[output.txid] = true;
}
// Otherwise, it's added to unconfirmedBalance.
else {
unconfirmedReceived += output.satoshis;
unconfirmedAppearanceIds[output.txid] = true;
unconfirmedBalance += output.satoshis; // TODO remove
}

if(spentDB || spentMempool) {
if(spentDB) {
totalSpent += outputs[i].satoshis;
balance -= outputs[i].satoshis;
} else if(!outputs[i].confirmations) {
unconfirmedBalance -= outputs[i].satoshis;
}
// We back it out of our balance and add to Spent if it's been spent in the DB,
// otherwise we toss it in unconfirmedSpent.
// Note unconfirmed txs don't affect balance.
if (spentDB) {
totalSpent += output.satoshis;
balance -= output.satoshis;
} else if (spentMempool) { // mempool
unconfirmedSpent += output.satoshis;
unconfirmedBalance -= output.satoshis; // TODO remove
}
}

for(var j = 0; j < inputs.length; j++) {
if (inputs[j].confirmations) {
appearanceIds[inputs[j].txid] = true;
} else {
unconfirmedAppearanceIds[outputs[j].txid] = true;
// If this is a change tx output, we don't count it as spent or received.
// We have to deduct from the right totals depending on confirmation status.
if (isChangeTx) {
if (confirmed) {
totalReceived -= output.satoshis;
totalSpent -= output.satoshis;
} else {
unconfirmedReceived -= output.satoshis;
unconfirmedSpent -= output.satoshis;
// intentionally no change to unconfirmedBalance. TODO remove this comment
}
}
}

var summary = {
totalReceived: totalReceived,
totalSpent: totalSpent,
balance: balance,
unconfirmedBalance: unconfirmedBalance,
unconfirmedReceived: unconfirmedReceived,
unconfirmedSpent: unconfirmedSpent,
unconfirmedBalance: unconfirmedBalance, // TODO remove, legacy
appearances: Object.keys(appearanceIds).length,
unconfirmedAppearances: Object.keys(unconfirmedAppearanceIds).length
};
Expand Down
Loading