From d36d21e7f8e259742f9aed93c1583df44fada541 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Thu, 17 Dec 2015 13:48:22 -0600 Subject: [PATCH 1/5] Don't count change transactions in totalSpent or totalReceived. This puts bitcore's calculations of spent and received in line with the behavior of other block explorers. Fixes #373 and bitpay/insight-api#351. --- lib/services/address/index.js | 19 +++++--- test/services/address/index.unit.js | 71 +++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index a557ba24c..3debf8953 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -1437,6 +1437,14 @@ AddressService.prototype.getAddressSummary = function(address, options, callback var unconfirmedAppearanceIds = {}; var txids = []; + for(var j = 0; j < inputs.length; j++) { + if (inputs[j].confirmations) { + appearanceIds[inputs[j].txid] = true; + } else { + unconfirmedAppearanceIds[outputs[j].txid] = true; + } + } + for(var i = 0; i < outputs.length; i++) { // Bitcoind's isSpent only works for confirmed transactions var spentDB = self.node.services.bitcoind.isSpent(outputs[i].txid, outputs[i].outputIndex); @@ -1445,6 +1453,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback outputs[i].outputIndex ); var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; + var isChangeTx = appearanceIds[outputs[i].txid]; txids.push(outputs[i]); @@ -1465,13 +1474,11 @@ AddressService.prototype.getAddressSummary = function(address, options, callback unconfirmedBalance -= outputs[i].satoshis; } } - } - 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. + if (isChangeTx) { + totalReceived -= outputs[i].satoshis; + totalSpent -= outputs[i].satoshis; } } diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index e7660649d..0177d6c9e 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -1828,5 +1828,76 @@ describe('Address Service', function() { done(); }); }); + describe('change transactions', function() { + var isSpentStub = sinon.stub(); + // https://github.com/sinonjs/sinon/issues/176 + isSpentStub.withArgs('c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff', 1).returns(true); + isSpentStub.withArgs('187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04', 0).returns(false); + var changeNode = { + datadir: 'testdir', + network: Networks.testnet, + services: { + bitcoind: { + // c0d txid is spent, 187 is not + isSpent: isSpentStub, + on: sinon.spy() + } + } + }; + var inputs = [ + { + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04", + "inputIndex": 0, + "height": 310818, + "confirmations": 315943 + } + ]; + + var outputs = [ + { + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff", + "outputIndex": 1, + "height": 310280, + "satoshis": 80000000, + "script": "a914c695365fc599b2960d92cfd720c04e054c4cb2f987", + "confirmations": 316481 + }, + { + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04", + "outputIndex": 0, + "height": 310818, + "satoshis": 59990000, + "script": "a914c695365fc599b2960d92cfd720c04e054c4cb2f987", + "confirmations": 315943 + } + ]; + var changeAS = new AddressService({ + node: changeNode + }); + changeAS.getInputs = sinon.stub().callsArgWith(2, null, inputs); + changeAS.getOutputs = sinon.stub().callsArgWith(2, null, outputs); + it('should not count toward totalReceived or totalSpent', function(done) { + changeAS.getAddressSummary('2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2', {}, function(err, summary) { + should.not.exist(err); + summary.totalReceived.should.equal(80000000); + summary.totalSpent.should.equal(20010000); + summary.balance.should.equal(59990000); + summary.unconfirmedBalance.should.equal(0); + summary.appearances.should.equal(2); + summary.unconfirmedAppearances.should.equal(0); + summary.txids.should.deep.equal( + [ + 'c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff', + '187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04' + ] + ); + done(); + }); + + }) + }); }); }); From f2e20232344c23bb00e03548af9825da71c70cae Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Thu, 17 Dec 2015 15:18:07 -0600 Subject: [PATCH 2/5] Ensure txs with multiple outputs create expected output. --- lib/services/address/index.js | 4 +- test/services/address/index.unit.js | 68 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 3debf8953..1090e9047 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -1433,11 +1433,13 @@ AddressService.prototype.getAddressSummary = function(address, options, callback var totalSpent = 0; var balance = 0; var unconfirmedBalance = 0; + var inputAppearanceIds = {}; var appearanceIds = {}; var unconfirmedAppearanceIds = {}; var txids = []; for(var j = 0; j < inputs.length; j++) { + inputAppearanceIds[inputs[j].txid] = true; // marked for change txs if (inputs[j].confirmations) { appearanceIds[inputs[j].txid] = true; } else { @@ -1453,7 +1455,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback outputs[i].outputIndex ); var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; - var isChangeTx = appearanceIds[outputs[i].txid]; + var isChangeTx = inputAppearanceIds[outputs[i].txid]; txids.push(outputs[i]); diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 0177d6c9e..27a785a79 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -1899,5 +1899,73 @@ describe('Address Service', function() { }) }); + + describe('multiple utxos', function() { + var changeNode = { + datadir: 'testdir', + network: Networks.testnet, + services: { + bitcoind: { + // c0d txid is spent, 187 is not + isSpent: sinon.stub().returns(false), + on: sinon.spy() + } + } + }; + var inputs = []; + + var outputs = [ + { + "address": "2NBMEXL8JwUrLxgdBKtFauiKivvHH8agdpK", + "txid": "4a14b9c1734b91681a22653ca890fe81429b49ffb4dac3e3f7bb8bee26c4b4a9", + "outputIndex": 1, + "height": 410826, + "satoshis": 110000000, + "script": "a914c69534eb2ce851c19dc0ff96e80b7a1c6298da4587", + "confirmations": 215952 + }, + { + "address": "2NBMEXL8JwUrLxgdBKtFauiKivvHH8agdpK", + "txid": "29daa591706b156e3b50e29b7586188f520d55b98db2806c3b243dd9620a7124", + "outputIndex": 0, + "height": 417750, + "satoshis": 141302, + "script": "a914c69534eb2ce851c19dc0ff96e80b7a1c6298da4587", + "confirmations": 209028 + }, + { + "address": "2NBMEXL8JwUrLxgdBKtFauiKivvHH8agdpK", + "txid": "29daa591706b156e3b50e29b7586188f520d55b98db2806c3b243dd9620a7124", + "outputIndex": 1, + "height": 417750, + "satoshis": 838698, + "script": "a914c69534eb2ce851c19dc0ff96e80b7a1c6298da4587", + "confirmations": 209028 + } + ]; + var changeAS = new AddressService({ + node: changeNode + }); + changeAS.getInputs = sinon.stub().callsArgWith(2, null, inputs); + changeAS.getOutputs = sinon.stub().callsArgWith(2, null, outputs); + it('should not create negative totalSpent due to multiple utxos in single tx', function(done) { + changeAS.getAddressSummary('2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2', {}, function(err, summary) { + should.not.exist(err); + summary.totalReceived.should.equal(110980000); + summary.totalSpent.should.equal(0); + summary.balance.should.equal(110980000); + summary.unconfirmedBalance.should.equal(0); + summary.appearances.should.equal(2); + summary.unconfirmedAppearances.should.equal(0); + summary.txids.should.deep.equal( + [ + '4a14b9c1734b91681a22653ca890fe81429b49ffb4dac3e3f7bb8bee26c4b4a9', + '29daa591706b156e3b50e29b7586188f520d55b98db2806c3b243dd9620a7124' + ] + ); + done(); + }); + }) + }); }); }); From 1f0032a4172993e7cfb9367aeed0b7f52ca63b5e Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Thu, 17 Dec 2015 17:14:37 -0600 Subject: [PATCH 3/5] Update regtest-node.js, which was expecting incorrect totals. --- integration/regtest-node.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration/regtest-node.js b/integration/regtest-node.js index 8b55db7f6..6a54cf388 100644 --- a/integration/regtest-node.js +++ b/integration/regtest-node.js @@ -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); From 37e8f812f9b057a9d62e6e4c964e36e52526255b Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 22 Dec 2015 10:04:02 -0600 Subject: [PATCH 4/5] Bugfixes re: change transactions in mempool, and additional tests. --- lib/services/address/index.js | 101 +++++++++--------- test/services/address/index.unit.js | 156 +++++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 52 deletions(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 1090e9047..5c9114467 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -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 @@ -1406,81 +1407,84 @@ 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 inputAppearanceIds = {}; var appearanceIds = {}; var unconfirmedAppearanceIds = {}; var txids = []; - for(var j = 0; j < inputs.length; j++) { - inputAppearanceIds[inputs[j].txid] = true; // marked for change txs - if (inputs[j].confirmations) { - appearanceIds[inputs[j].txid] = true; + 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[outputs[j].txid] = true; + unconfirmedAppearanceIds[input.txid] = true; } } - for(var i = 0; i < outputs.length; i++) { + 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[outputs[i].txid]; + 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; } - 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; } // 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) { - totalReceived -= outputs[i].satoshis; - totalSpent -= outputs[i].satoshis; + if (confirmed) { + totalReceived -= output.satoshis; + totalSpent -= output.satoshis; + } else { + unconfirmedReceived -= output.satoshis; + unconfirmedSpent -= output.satoshis; + } } } @@ -1488,7 +1492,8 @@ AddressService.prototype.getAddressSummary = function(address, options, callback totalReceived: totalReceived, totalSpent: totalSpent, balance: balance, - unconfirmedBalance: unconfirmedBalance, + unconfirmedReceived: unconfirmedReceived, + unconfirmedSpent: unconfirmedSpent, appearances: Object.keys(appearanceIds).length, unconfirmedAppearances: Object.keys(unconfirmedAppearanceIds).length }; diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 27a785a79..28bfa46a7 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -1803,7 +1803,8 @@ describe('Address Service', function() { summary.totalReceived.should.equal(3487110); summary.totalSpent.should.equal(0); summary.balance.should.equal(3487110); - summary.unconfirmedBalance.should.equal(0); + summary.unconfirmedReceived.should.equal(0); + summary.unconfirmedSpent.should.equal(3487110); summary.appearances.should.equal(1); summary.unconfirmedAppearances.should.equal(1); summary.txids.should.deep.equal( @@ -1821,13 +1822,84 @@ describe('Address Service', function() { summary.totalReceived.should.equal(3487110); summary.totalSpent.should.equal(0); summary.balance.should.equal(3487110); - summary.unconfirmedBalance.should.equal(0); + summary.unconfirmedReceived.should.equal(0); + summary.unconfirmedSpent.should.equal(3487110); summary.appearances.should.equal(1); summary.unconfirmedAppearances.should.equal(1); should.not.exist(summary.txids); done(); }); }); + + describe('unconfirmed (mempool) transactions', function() { + var node = { + datadir: 'testdir', + network: Networks.testnet, + services: { + bitcoind: { + isSpent: sinon.stub().returns(false), + on: sinon.spy() + } + } + }; + // Technically this was a change tx, but for this test we're removing the input + // so we can just test a mempool tx to some other addr + var inputs = []; + + var outputs = [ + { // not spent + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff", + "outputIndex": 1, + "height": 310280, + "satoshis": 80000000, + "script": "a914c695365fc599b2960d92cfd720c04e054c4cb2f987", + "confirmations": 316481 + }, + { // spent + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04", + "outputIndex": 0, + "height": 310818, + "satoshis": 59990000, + "script": "a914c695365fc599b2960d92cfd720c04e054c4cb2f987", + "confirmations": 315943 + } + ]; + var mempoolAS = new AddressService({ + node: node + }); + mempoolAS.getInputs = sinon.stub().callsArgWith(2, null, inputs); + mempoolAS.getOutputs = sinon.stub().callsArgWith(2, null, outputs); + // Add to spent mempool + var spentIndexSyncKey = mempoolAS._encodeSpentIndexSyncKey(new Buffer(outputs[1].txid, 'hex'), 0); + mempoolAS.mempoolSpentIndex[spentIndexSyncKey] = true; + + it('should add to unconfirmedSpent and not deduct from balance until conf', function(done) { + mempoolAS.getAddressSummary('2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2', {}, function(err, summary) { + should.not.exist(err); + summary.totalReceived.should.equal(139990000); + summary.totalSpent.should.equal(0); + summary.balance.should.equal(139990000); + summary.unconfirmedReceived.should.equal(0); + summary.unconfirmedSpent.should.equal(59990000); + summary.appearances.should.equal(2); + summary.unconfirmedAppearances.should.equal(0); + summary.txids.should.deep.equal( + [ + 'c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff', + '187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04' + ] + ); + done(); + }); + }) + }); + + // In this test, we've received 0.8 BTC. + // We've then spent that output to send 0.2001 elsewhere and 0.5999 back to ourselves. + // The 0.5999 is unspent, so we sould have a balance of 0.5999 with 0.2001 spent. + // Previously, this would count again as received funds which is not like any other explorer. describe('change transactions', function() { var isSpentStub = sinon.stub(); // https://github.com/sinonjs/sinon/issues/176 @@ -1885,7 +1957,8 @@ describe('Address Service', function() { summary.totalReceived.should.equal(80000000); summary.totalSpent.should.equal(20010000); summary.balance.should.equal(59990000); - summary.unconfirmedBalance.should.equal(0); + summary.unconfirmedReceived.should.equal(0); + summary.unconfirmedSpent.should.equal(0); summary.appearances.should.equal(2); summary.unconfirmedAppearances.should.equal(0); summary.txids.should.deep.equal( @@ -1896,7 +1969,81 @@ describe('Address Service', function() { ); done(); }); + }) + }); + + // In this test, we have: + // * One confirmed output of 0.8 BTC sent to our address + // * One unconfirmed (mempool) output of 0.5999 BTC sent to our address, + // but this is a change tx. + // So none of the txs are spent in the db, but the 0.8 one is spent in the mempool. + // Most block explorers show this as a balance of 0.8 with 0.2001 unconfirmed spent. + describe('change transactions with mempool', function() { + var changeNode = { + datadir: 'testdir', + network: Networks.testnet, + services: { + bitcoind: { + isSpent: sinon.stub().returns(false), + on: sinon.spy() + } + } + }; + var inputs = [ + { + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04", + "inputIndex": 0, + "height": 626761, + "confirmations": 0 + } + ]; + var outputs = [ + { + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff", + "outputIndex": 1, + "height": 310280, + "satoshis": 80000000, + "script": "a914c695365fc599b2960d92cfd720c04e054c4cb2f987", + "confirmations": 316481 + }, + { + "address": "2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2", + "txid": "187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04", + "outputIndex": 0, + "height": 626761, + "satoshis": 59990000, + "script": "a914c695365fc599b2960d92cfd720c04e054c4cb2f987", + "confirmations": 0 + } + ]; + var changeAS = new AddressService({ + node: changeNode + }); + changeAS.getInputs = sinon.stub().callsArgWith(2, null, inputs); + changeAS.getOutputs = sinon.stub().callsArgWith(2, null, outputs); + var spentIndexSyncKey = changeAS._encodeSpentIndexSyncKey(new Buffer(outputs[0].txid, 'hex'), 1); + changeAS.mempoolSpentIndex[spentIndexSyncKey] = true; + it('should not deduct from totalReceived or add to totalSpent, and should add to unconfirmedSpent', function(done) { + changeAS.getAddressSummary('2NBMEXj3BM7C2k4HCjfqn1Q4mwezNUzmrs2', {}, function(err, summary) { + should.not.exist(err); + summary.totalReceived.should.equal(80000000); + summary.totalSpent.should.equal(0); + summary.balance.should.equal(80000000); + summary.unconfirmedReceived.should.equal(0); + summary.unconfirmedSpent.should.equal(20010000); + summary.appearances.should.equal(1); + summary.unconfirmedAppearances.should.equal(1); + summary.txids.should.deep.equal( + [ + 'c0dfe181d45094c5c4e99b56be0699c4a6423b9e0086f6f8096ea8a5bfa57cff', + '187ec93c565522f17605a8591a51c602eb1463f42df4b169c855a591ec952a04' + ] + ); + done(); + }); }) }); @@ -1954,7 +2101,8 @@ describe('Address Service', function() { summary.totalReceived.should.equal(110980000); summary.totalSpent.should.equal(0); summary.balance.should.equal(110980000); - summary.unconfirmedBalance.should.equal(0); + summary.unconfirmedReceived.should.equal(0); + summary.unconfirmedSpent.should.equal(0); summary.appearances.should.equal(2); summary.unconfirmedAppearances.should.equal(0); summary.txids.should.deep.equal( From c77147c8b9dab31e272fba6091a44297b81f0cb9 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 22 Dec 2015 10:17:08 -0600 Subject: [PATCH 5/5] Keep unconfirmedBalance in for backcompat with older APIs. TODO revert --- lib/services/address/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 5c9114467..896b797d6 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -1424,6 +1424,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback var balance = 0; var unconfirmedReceived = 0; var unconfirmedSpent = 0; + var unconfirmedBalance = 0; // TODO remove this, for backcompat with insight-API var inputAppearanceIds = {}; var appearanceIds = {}; var unconfirmedAppearanceIds = {}; @@ -1463,6 +1464,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback else { unconfirmedReceived += output.satoshis; unconfirmedAppearanceIds[output.txid] = true; + unconfirmedBalance += output.satoshis; // TODO remove } // We back it out of our balance and add to Spent if it's been spent in the DB, @@ -1473,6 +1475,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback balance -= output.satoshis; } else if (spentMempool) { // mempool unconfirmedSpent += output.satoshis; + unconfirmedBalance -= output.satoshis; // TODO remove } // If this is a change tx output, we don't count it as spent or received. @@ -1484,6 +1487,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback } else { unconfirmedReceived -= output.satoshis; unconfirmedSpent -= output.satoshis; + // intentionally no change to unconfirmedBalance. TODO remove this comment } } } @@ -1494,6 +1498,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback balance: balance, unconfirmedReceived: unconfirmedReceived, unconfirmedSpent: unconfirmedSpent, + unconfirmedBalance: unconfirmedBalance, // TODO remove, legacy appearances: Object.keys(appearanceIds).length, unconfirmedAppearances: Object.keys(unconfirmedAppearanceIds).length };