From e7e33313cf12612b4957d75ac183ecd41e706e87 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 8 Feb 2016 12:41:49 -0500 Subject: [PATCH 01/13] add noBalance options + mempoolAddressIndex --- lib/services/address/history.js | 6 +- lib/services/address/index.js | 177 ++++++++++++++++++-------------- 2 files changed, 106 insertions(+), 77 deletions(-) diff --git a/lib/services/address/history.js b/lib/services/address/history.js index 998dbfde..b7750068 100644 --- a/lib/services/address/history.js +++ b/lib/services/address/history.js @@ -44,6 +44,7 @@ function AddressHistory(args) { AddressHistory.prototype._mergeAndSortTxids = function(summaries) { var appearanceIds = {}; var unconfirmedAppearanceIds = {}; + for (var i = 0; i < summaries.length; i++) { var summary = summaries[i]; for (var key in summary.appearanceIds) { @@ -79,6 +80,8 @@ AddressHistory.prototype.get = function(callback) { return callback(new TypeError('Maximum number of addresses (' + this.maxAddressesQuery + ') exceeded')); } + this.options.noBalance = true; + if (this.addresses.length === 1) { var address = this.addresses[0]; self.node.services.address.getAddressSummary(address, this.options, function(err, summary) { @@ -89,10 +92,11 @@ AddressHistory.prototype.get = function(callback) { }); } else { var opts = _.clone(this.options); + opts.fullTxList = true; async.mapLimit( self.addresses, - self.maxAddressesLimit, + self.maxaddressesLimit, function(address, next) { self.node.services.address.getAddressSummary(address, opts, next); }, diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 61eed5ef..f67fa8ae 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -57,6 +57,7 @@ var AddressService = function(options) { } this.mempoolIndex = null; // Used for larger mempool indexes this.mempoolSpentIndex = {}; // Used for small quick synchronous lookups + this.mempoolAddressIndex = {}; // Used to check if an address is on the spend pool }; inherits(AddressService, BaseService); @@ -70,6 +71,7 @@ AddressService.prototype.start = function(callback) { var self = this; async.series([ + function(next) { // Flush any existing mempool index if (fs.existsSync(self.mempoolIndexPath)) { @@ -88,8 +90,7 @@ AddressService.prototype.start = function(callback) { }, function(next) { self.mempoolIndex = levelup( - self.mempoolIndexPath, - { + self.mempoolIndexPath, { db: self.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary', @@ -154,20 +155,17 @@ AddressService.prototype.getAPIMethods = function() { * Called by the Bus to get the available events for this service. */ AddressService.prototype.getPublishEvents = function() { - return [ - { - name: 'address/transaction', - scope: this, - subscribe: this.subscribe.bind(this, 'address/transaction'), - unsubscribe: this.unsubscribe.bind(this, 'address/transaction') - }, - { - name: 'address/balance', - scope: this, - subscribe: this.subscribe.bind(this, 'address/balance'), - unsubscribe: this.unsubscribe.bind(this, 'address/balance') - } - ]; + return [{ + name: 'address/transaction', + scope: this, + subscribe: this.subscribe.bind(this, 'address/transaction'), + unsubscribe: this.unsubscribe.bind(this, 'address/transaction') + }, { + name: 'address/balance', + scope: this, + subscribe: this.subscribe.bind(this, 'address/balance'), + unsubscribe: this.unsubscribe.bind(this, 'address/balance') + }]; }; /** @@ -305,6 +303,14 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { continue; } + var hashBufferHex = addressInfo.hashBuffer.toString('hex'); + + if (add) { + this.mempoolAddressIndex[hashBufferHex] = true; + } else { + delete this.mempoolAddressIndex[hashBufferHex]; + } + // Update output index var outputIndexBuffer = new Buffer(4); outputIndexBuffer.writeUInt32BE(outputIndex); @@ -397,6 +403,12 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { value: inputValue }); + var inputHashBufferHex = inputHashBuffer.toString('hex'); + if (add) { + this.mempoolAddressIndex[inputHashBufferHex] = true; + } else { + delete this.mempoolAddressIndex[inputHashBufferHex]; + } } if (!callback) { @@ -446,7 +458,7 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { var script = output.script; - if(!script) { + if (!script) { log.debug('Invalid script'); continue; } @@ -462,7 +474,7 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { // less than the mean of the 11 previous blocks) and not greater than 2 // hours in the future. var key = encoding.encodeOutputKey(addressInfo.hashBuffer, addressInfo.hashTypeBuffer, - height, txidBuffer, outputIndex); + height, txidBuffer, outputIndex); var value = encoding.encodeOutputValue(output.satoshis, output._scriptBuffer); operations.push({ type: action, @@ -494,11 +506,11 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { this.transactionEventHandler(txmessages[addressKey]); } - if(tx.isCoinbase()) { + if (tx.isCoinbase()) { continue; } - for(var inputIndex = 0; inputIndex < inputs.length; inputIndex++) { + for (var inputIndex = 0; inputIndex < inputs.length; inputIndex++) { var input = inputs[inputIndex]; var inputHash; @@ -560,14 +572,14 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { * @param {Boolean} obj.rejected - If the transaction was not accepted in the mempool */ AddressService.prototype.transactionEventHandler = function(obj) { - if(this.subscriptions['address/transaction'][obj.addressInfo.hashHex]) { + if (this.subscriptions['address/transaction'][obj.addressInfo.hashHex]) { var emitters = this.subscriptions['address/transaction'][obj.addressInfo.hashHex]; var address = new Address({ hashBuffer: obj.addressInfo.hashBuffer, network: this.node.network, type: obj.addressInfo.addressType }); - for(var i = 0; i < emitters.length; i++) { + for (var i = 0; i < emitters.length; i++) { emitters[i].emit('address/transaction', { rejected: obj.rejected, height: obj.height, @@ -591,7 +603,7 @@ AddressService.prototype.transactionEventHandler = function(obj) { * @param {String} obj.addressType */ AddressService.prototype.balanceEventHandler = function(block, obj) { - if(this.subscriptions['address/balance'][obj.hashHex]) { + if (this.subscriptions['address/balance'][obj.hashHex]) { var emitters = this.subscriptions['address/balance'][obj.hashHex]; var address = new Address({ hashBuffer: obj.hashBuffer, @@ -599,10 +611,10 @@ AddressService.prototype.balanceEventHandler = function(block, obj) { type: obj.addressType }); this.getBalance(address, true, function(err, balance) { - if(err) { + if (err) { return this.emit(err); } - for(var i = 0; i < emitters.length; i++) { + for (var i = 0; i < emitters.length; i++) { emitters[i].emit('address/balance', address, balance, block); } }); @@ -621,9 +633,9 @@ AddressService.prototype.subscribe = function(name, emitter, addresses) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); $.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses'); - for(var i = 0; i < addresses.length; i++) { + for (var i = 0; i < addresses.length; i++) { var hashHex = bitcore.Address(addresses[i]).hashBuffer.toString('hex'); - if(!this.subscriptions[name][hashHex]) { + if (!this.subscriptions[name][hashHex]) { this.subscriptions[name][hashHex] = []; } this.subscriptions[name][hashHex].push(emitter); @@ -641,16 +653,16 @@ AddressService.prototype.unsubscribe = function(name, emitter, addresses) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); $.checkArgument(Array.isArray(addresses) || _.isUndefined(addresses), 'Second argument is expected to be an Array of addresses or undefined'); - if(!addresses) { + if (!addresses) { return this.unsubscribeAll(name, emitter); } - for(var i = 0; i < addresses.length; i++) { + for (var i = 0; i < addresses.length; i++) { var hashHex = bitcore.Address(addresses[i]).hashBuffer.toString('hex'); - if(this.subscriptions[name][hashHex]) { + if (this.subscriptions[name][hashHex]) { var emitters = this.subscriptions[name][hashHex]; var index = emitters.indexOf(emitter); - if(index > -1) { + if (index > -1) { emitters.splice(index, 1); } } @@ -665,10 +677,10 @@ AddressService.prototype.unsubscribe = function(name, emitter, addresses) { AddressService.prototype.unsubscribeAll = function(name, emitter) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); - for(var hashHex in this.subscriptions[name]) { + for (var hashHex in this.subscriptions[name]) { var emitters = this.subscriptions[name][hashHex]; var index = emitters.indexOf(emitter); - if(index > -1) { + if (index > -1) { emitters.splice(index, 1); } } @@ -683,7 +695,7 @@ AddressService.prototype.unsubscribeAll = function(name, emitter) { */ AddressService.prototype.getBalance = function(address, queryMempool, callback) { this.getUnspentOutputs(address, queryMempool, function(err, outputs) { - if(err) { + if (err) { return callback(err); } @@ -852,7 +864,7 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) { stream.on('data', function(input) { inputs.push(input); if (inputs.length > self.maxInputsQueryLength) { - log.warn('Tried to query too many inputs (' + self.maxInputsQueryLength + ') for address '+ addressStr); + log.warn('Tried to query too many inputs (' + self.maxInputsQueryLength + ') for address ' + addressStr); error = new Error('Maximum number of inputs (' + self.maxInputsQueryLength + ') per query reached'); stream.end(); } @@ -871,7 +883,7 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) { return callback(error); } - if(options.queryMempool) { + if (options.queryMempool) { self._getInputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolInputs) { if (err) { return callback(err); @@ -1092,7 +1104,7 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) { return callback(error); } - if(options.queryMempool) { + if (options.queryMempool) { self._getOutputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolOutputs) { if (err) { return callback(err); @@ -1176,7 +1188,7 @@ AddressService.prototype._getOutputsMempool = function(addressStr, hashBuffer, h AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, callback) { var self = this; - if(!Array.isArray(addresses)) { + if (!Array.isArray(addresses)) { addresses = [addresses]; } @@ -1184,9 +1196,9 @@ AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, c async.eachSeries(addresses, function(address, next) { self.getUnspentOutputsForAddress(address, queryMempool, function(err, unspents) { - if(err && err instanceof errors.NoOutputs) { + if (err && err instanceof errors.NoOutputs) { return next(); - } else if(err) { + } else if (err) { return next(err); } @@ -1208,10 +1220,12 @@ AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMe var self = this; - this.getOutputs(address, {queryMempool: queryMempool}, function(err, outputs) { + this.getOutputs(address, { + queryMempool: queryMempool + }, function(err, outputs) { if (err) { return callback(err); - } else if(!outputs.length) { + } else if (!outputs.length) { return callback(new errors.NoOutputs('Address ' + address + ' has no outputs'), []); } @@ -1336,6 +1350,7 @@ AddressService.prototype.getAddressSummary = function(addressArg, options, callb } async.waterfall([ + function(next) { self._getAddressConfirmedSummary(address, options, next); }, @@ -1357,9 +1372,7 @@ AddressService.prototype.getAddressSummary = function(addressArg, options, callb var seconds = Math.round(timeDelta / 1000); log.warn('Slow (' + seconds + 's) getAddressSummary request for address: ' + address.toString()); } - callback(null, summary); - }); }; @@ -1375,6 +1388,7 @@ AddressService.prototype._getAddressConfirmedSummary = function(address, options }; async.waterfall([ + function(next) { self._getAddressConfirmedInputsSummary(address, baseResult, options, next); }, @@ -1421,8 +1435,8 @@ AddressService.prototype._getAddressConfirmedInputsSummary = function(address, r AddressService.prototype._getAddressConfirmedOutputsSummary = function(address, result, options, callback) { $.checkArgument(address instanceof Address); $.checkArgument(!_.isUndefined(result) && - !_.isUndefined(result.appearanceIds) && - !_.isUndefined(result.unconfirmedAppearanceIds)); + !_.isUndefined(result.appearanceIds) && + !_.isUndefined(result.unconfirmedAppearanceIds)); var self = this; var count = 0; @@ -1434,25 +1448,27 @@ AddressService.prototype._getAddressConfirmedOutputsSummary = function(address, var txid = output.txid; var outputIndex = output.outputIndex; - // Bitcoind's isSpent only works for confirmed transactions - var spentDB = self.node.services.bitcoind.isSpent(txid, outputIndex); - result.totalReceived += output.satoshis; - result.appearanceIds[txid] = output.height; + if (!options.noBalance) { + // Bitcoind's isSpent only works for confirmed transactions + var spentDB = self.node.services.bitcoind.isSpent(txid, outputIndex); + result.totalReceived += output.satoshis; + result.appearanceIds[txid] = output.height; - if (!spentDB) { - result.balance += output.satoshis; - } + if (!spentDB) { + result.balance += output.satoshis; + } - if (options.queryMempool) { - // Check to see if this output is spent in the mempool and if so - // we will subtract it from the unconfirmedBalance (a.k.a unconfirmedDelta) - var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( - new Buffer(txid, 'hex'), // TODO: get buffer directly - outputIndex - ); - var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; - if (spentMempool) { - result.unconfirmedBalance -= output.satoshis; + if (options.queryMempool) { + // Check to see if this output is spent in the mempool and if so + // we will subtract it from the unconfirmedBalance (a.k.a unconfirmedDelta) + var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( + new Buffer(txid, 'hex'), // TODO: get buffer directly + outputIndex + ); + var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; + if (spentMempool) { + result.unconfirmedBalance -= output.satoshis; + } } } @@ -1504,38 +1520,47 @@ AddressService.prototype._getAddressMempoolSummary = function(address, options, var addressStr = address.toString(); var hashBuffer = address.hashBuffer; var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type]; + var hashBufferHex = hashBuffer.toString('hex'); + + if (!this.mempoolAddressIndex[hashBufferHex]) { + return callback(null, result); + } async.waterfall([ + function(next) { self._getInputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolInputs) { if (err) { return next(err); } - for(var i = 0; i < mempoolInputs.length; i++) { + for (var i = 0; i < mempoolInputs.length; i++) { var input = mempoolInputs[i]; result.unconfirmedAppearanceIds[input.txid] = input.timestamp; } next(null, result); }); - }, function(result, next) { + }, + function(result, next) { self._getOutputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolOutputs) { if (err) { return next(err); } - for(var i = 0; i < mempoolOutputs.length; i++) { - var output = mempoolOutputs[i]; + if (!options.noBalance) { + for (var i = 0; i < mempoolOutputs.length; i++) { + var output = mempoolOutputs[i]; - result.unconfirmedAppearanceIds[output.txid] = output.timestamp; + result.unconfirmedAppearanceIds[output.txid] = output.timestamp; - var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( - new Buffer(output.txid, 'hex'), // TODO: get buffer directly - output.outputIndex - ); - var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; - // Only add this to the balance if it's not spent in the mempool already - if (!spentMempool) { - result.unconfirmedBalance += output.satoshis; + var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( + new Buffer(output.txid, 'hex'), // TODO: get buffer directly + output.outputIndex + ); + var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; + // Only add this to the balance if it's not spent in the mempool already + if (!spentMempool) { + result.unconfirmedBalance += output.satoshis; + } } } next(null, result); From c1d3f351f23c7834599301aea62e740c1573ec41 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 8 Feb 2016 13:21:53 -0500 Subject: [PATCH 02/13] add address index to mempool + noBalance options --- lib/services/address/history.js | 2 +- test/services/address/history.unit.js | 3 ++- test/services/address/index.unit.js | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/services/address/history.js b/lib/services/address/history.js index b7750068..946eac26 100644 --- a/lib/services/address/history.js +++ b/lib/services/address/history.js @@ -96,7 +96,7 @@ AddressHistory.prototype.get = function(callback) { opts.fullTxList = true; async.mapLimit( self.addresses, - self.maxaddressesLimit, + self.maxAddressesLimit, function(address, next) { self.node.services.address.getAddressSummary(address, opts, next); }, diff --git a/test/services/address/history.unit.js b/test/services/address/history.unit.js index 6ddb7369..917ac735 100644 --- a/test/services/address/history.unit.js +++ b/test/services/address/history.unit.js @@ -154,7 +154,8 @@ describe('Address Service History', function() { history.node.services.address.getAddressSummary.callCount.should.equal(2); history.node.services.address.getAddressSummary.args[0][0].should.equal(address); history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({ - fullTxList: true + fullTxList: true, + noBalance: true, }); history._paginateWithDetails.callCount.should.equal(1); history._paginateWithDetails.args[0][0].should.equal(txids); diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index a35614df..929c0c84 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -2519,6 +2519,10 @@ describe('Address Service', function() { 0 ); as.mempoolSpentIndex[spentIndexSyncKey] = true; + + var hashBufferHex = address.hashBuffer.toString('hex'); + as.mempoolAddressIndex[hashBufferHex] = true; + as._getInputsMempool = sinon.stub().callsArgWith(3, null, mempoolInputs); as._getOutputsMempool = sinon.stub().callsArgWith(3, null, mempoolOutputs); as._getAddressMempoolSummary(address, options, resultBase, function(err, result) { From c65c2bad205dbd437e3ea4aa29185c09883e6409 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 8 Feb 2016 13:40:27 -0500 Subject: [PATCH 03/13] add mempoolADdressIndex test --- test/services/address/index.unit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 929c0c84..4d4061e1 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -1977,6 +1977,7 @@ describe('Address Service', function() { am.mempoolIndex.batch = function(operations, callback) { callback.should.be.a('function'); Object.keys(am.mempoolSpentIndex).length.should.equal(14); + Object.keys(am.mempoolAddressIndex).length.should.equal(5); for (var i = 0; i < operations.length; i++) { operations[i].type.should.equal('put'); } From dae5c9d3d555044a00d04597c8e5bc6917c0fdb2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 8 Feb 2016 15:07:32 -0500 Subject: [PATCH 04/13] fix regtests --- lib/services/address/history.js | 6 +++--- lib/services/address/index.js | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/services/address/history.js b/lib/services/address/history.js index 946eac26..2d1dcd34 100644 --- a/lib/services/address/history.js +++ b/lib/services/address/history.js @@ -80,18 +80,18 @@ AddressHistory.prototype.get = function(callback) { return callback(new TypeError('Maximum number of addresses (' + this.maxAddressesQuery + ') exceeded')); } - this.options.noBalance = true; + var opts = _.clone(this.options); + opts.noBalance = true; if (this.addresses.length === 1) { var address = this.addresses[0]; - self.node.services.address.getAddressSummary(address, this.options, function(err, summary) { + self.node.services.address.getAddressSummary(address, opts, function(err, summary) { if (err) { return callback(err); } return self._paginateWithDetails.call(self, summary.txids, callback); }); } else { - var opts = _.clone(this.options); opts.fullTxList = true; async.mapLimit( diff --git a/lib/services/address/index.js b/lib/services/address/index.js index f67fa8ae..94c4db9f 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -1447,12 +1447,13 @@ AddressService.prototype._getAddressConfirmedOutputsSummary = function(address, var txid = output.txid; var outputIndex = output.outputIndex; + result.totalReceived += output.satoshis; + result.appearanceIds[txid] = output.height; if (!options.noBalance) { + // Bitcoind's isSpent only works for confirmed transactions var spentDB = self.node.services.bitcoind.isSpent(txid, outputIndex); - result.totalReceived += output.satoshis; - result.appearanceIds[txid] = output.height; if (!spentDB) { result.balance += output.satoshis; @@ -1546,12 +1547,12 @@ AddressService.prototype._getAddressMempoolSummary = function(address, options, if (err) { return next(err); } - if (!options.noBalance) { - for (var i = 0; i < mempoolOutputs.length; i++) { - var output = mempoolOutputs[i]; + for (var i = 0; i < mempoolOutputs.length; i++) { + var output = mempoolOutputs[i]; - result.unconfirmedAppearanceIds[output.txid] = output.timestamp; + result.unconfirmedAppearanceIds[output.txid] = output.timestamp; + if (!options.noBalance) { var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( new Buffer(output.txid, 'hex'), // TODO: get buffer directly output.outputIndex From 9f87156adc7c2710d7a355b32de50dd38c75f2f3 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 9 Feb 2016 10:26:09 -0500 Subject: [PATCH 05/13] fix format --- lib/services/address/index.js | 116 +++++++++++++------------- test/services/address/history.unit.js | 5 +- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 94c4db9f..da903766 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -90,7 +90,8 @@ AddressService.prototype.start = function(callback) { }, function(next) { self.mempoolIndex = levelup( - self.mempoolIndexPath, { + self.mempoolIndexPath, + { db: self.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary', @@ -155,17 +156,20 @@ AddressService.prototype.getAPIMethods = function() { * Called by the Bus to get the available events for this service. */ AddressService.prototype.getPublishEvents = function() { - return [{ - name: 'address/transaction', - scope: this, - subscribe: this.subscribe.bind(this, 'address/transaction'), - unsubscribe: this.unsubscribe.bind(this, 'address/transaction') - }, { - name: 'address/balance', - scope: this, - subscribe: this.subscribe.bind(this, 'address/balance'), - unsubscribe: this.unsubscribe.bind(this, 'address/balance') - }]; + return [ + { + name: 'address/transaction', + scope: this, + subscribe: this.subscribe.bind(this, 'address/transaction'), + unsubscribe: this.unsubscribe.bind(this, 'address/transaction') + }, + { + name: 'address/balance', + scope: this, + subscribe: this.subscribe.bind(this, 'address/balance'), + unsubscribe: this.unsubscribe.bind(this, 'address/balance') + } + ]; }; /** @@ -305,7 +309,7 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { var hashBufferHex = addressInfo.hashBuffer.toString('hex'); - if (add) { + if(add) { this.mempoolAddressIndex[hashBufferHex] = true; } else { delete this.mempoolAddressIndex[hashBufferHex]; @@ -404,7 +408,7 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { }); var inputHashBufferHex = inputHashBuffer.toString('hex'); - if (add) { + if(add) { this.mempoolAddressIndex[inputHashBufferHex] = true; } else { delete this.mempoolAddressIndex[inputHashBufferHex]; @@ -458,7 +462,7 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { var script = output.script; - if (!script) { + if(!script) { log.debug('Invalid script'); continue; } @@ -474,7 +478,7 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { // less than the mean of the 11 previous blocks) and not greater than 2 // hours in the future. var key = encoding.encodeOutputKey(addressInfo.hashBuffer, addressInfo.hashTypeBuffer, - height, txidBuffer, outputIndex); + height, txidBuffer, outputIndex); var value = encoding.encodeOutputValue(output.satoshis, output._scriptBuffer); operations.push({ type: action, @@ -506,11 +510,11 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { this.transactionEventHandler(txmessages[addressKey]); } - if (tx.isCoinbase()) { + if(tx.isCoinbase()) { continue; } - for (var inputIndex = 0; inputIndex < inputs.length; inputIndex++) { + for(var inputIndex = 0; inputIndex < inputs.length; inputIndex++) { var input = inputs[inputIndex]; var inputHash; @@ -572,14 +576,14 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { * @param {Boolean} obj.rejected - If the transaction was not accepted in the mempool */ AddressService.prototype.transactionEventHandler = function(obj) { - if (this.subscriptions['address/transaction'][obj.addressInfo.hashHex]) { + if(this.subscriptions['address/transaction'][obj.addressInfo.hashHex]) { var emitters = this.subscriptions['address/transaction'][obj.addressInfo.hashHex]; var address = new Address({ hashBuffer: obj.addressInfo.hashBuffer, network: this.node.network, type: obj.addressInfo.addressType }); - for (var i = 0; i < emitters.length; i++) { + for(var i = 0; i < emitters.length; i++) { emitters[i].emit('address/transaction', { rejected: obj.rejected, height: obj.height, @@ -603,7 +607,7 @@ AddressService.prototype.transactionEventHandler = function(obj) { * @param {String} obj.addressType */ AddressService.prototype.balanceEventHandler = function(block, obj) { - if (this.subscriptions['address/balance'][obj.hashHex]) { + if(this.subscriptions['address/balance'][obj.hashHex]) { var emitters = this.subscriptions['address/balance'][obj.hashHex]; var address = new Address({ hashBuffer: obj.hashBuffer, @@ -611,10 +615,10 @@ AddressService.prototype.balanceEventHandler = function(block, obj) { type: obj.addressType }); this.getBalance(address, true, function(err, balance) { - if (err) { + if(err) { return this.emit(err); } - for (var i = 0; i < emitters.length; i++) { + for(var i = 0; i < emitters.length; i++) { emitters[i].emit('address/balance', address, balance, block); } }); @@ -633,9 +637,9 @@ AddressService.prototype.subscribe = function(name, emitter, addresses) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); $.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses'); - for (var i = 0; i < addresses.length; i++) { + for(var i = 0; i < addresses.length; i++) { var hashHex = bitcore.Address(addresses[i]).hashBuffer.toString('hex'); - if (!this.subscriptions[name][hashHex]) { + if(!this.subscriptions[name][hashHex]) { this.subscriptions[name][hashHex] = []; } this.subscriptions[name][hashHex].push(emitter); @@ -653,16 +657,16 @@ AddressService.prototype.unsubscribe = function(name, emitter, addresses) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); $.checkArgument(Array.isArray(addresses) || _.isUndefined(addresses), 'Second argument is expected to be an Array of addresses or undefined'); - if (!addresses) { + if(!addresses) { return this.unsubscribeAll(name, emitter); } - for (var i = 0; i < addresses.length; i++) { + for(var i = 0; i < addresses.length; i++) { var hashHex = bitcore.Address(addresses[i]).hashBuffer.toString('hex'); - if (this.subscriptions[name][hashHex]) { + if(this.subscriptions[name][hashHex]) { var emitters = this.subscriptions[name][hashHex]; var index = emitters.indexOf(emitter); - if (index > -1) { + if(index > -1) { emitters.splice(index, 1); } } @@ -677,10 +681,10 @@ AddressService.prototype.unsubscribe = function(name, emitter, addresses) { AddressService.prototype.unsubscribeAll = function(name, emitter) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); - for (var hashHex in this.subscriptions[name]) { + for(var hashHex in this.subscriptions[name]) { var emitters = this.subscriptions[name][hashHex]; var index = emitters.indexOf(emitter); - if (index > -1) { + if(index > -1) { emitters.splice(index, 1); } } @@ -695,7 +699,7 @@ AddressService.prototype.unsubscribeAll = function(name, emitter) { */ AddressService.prototype.getBalance = function(address, queryMempool, callback) { this.getUnspentOutputs(address, queryMempool, function(err, outputs) { - if (err) { + if(err) { return callback(err); } @@ -864,7 +868,7 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) { stream.on('data', function(input) { inputs.push(input); if (inputs.length > self.maxInputsQueryLength) { - log.warn('Tried to query too many inputs (' + self.maxInputsQueryLength + ') for address ' + addressStr); + log.warn('Tried to query too many inputs (' + self.maxInputsQueryLength + ') for address '+ addressStr); error = new Error('Maximum number of inputs (' + self.maxInputsQueryLength + ') per query reached'); stream.end(); } @@ -883,7 +887,7 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) { return callback(error); } - if (options.queryMempool) { + if(options.queryMempool) { self._getInputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolInputs) { if (err) { return callback(err); @@ -1104,7 +1108,7 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) { return callback(error); } - if (options.queryMempool) { + if(options.queryMempool) { self._getOutputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolOutputs) { if (err) { return callback(err); @@ -1188,7 +1192,7 @@ AddressService.prototype._getOutputsMempool = function(addressStr, hashBuffer, h AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, callback) { var self = this; - if (!Array.isArray(addresses)) { + if(!Array.isArray(addresses)) { addresses = [addresses]; } @@ -1196,9 +1200,9 @@ AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, c async.eachSeries(addresses, function(address, next) { self.getUnspentOutputsForAddress(address, queryMempool, function(err, unspents) { - if (err && err instanceof errors.NoOutputs) { + if(err && err instanceof errors.NoOutputs) { return next(); - } else if (err) { + } else if(err) { return next(err); } @@ -1220,12 +1224,10 @@ AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMe var self = this; - this.getOutputs(address, { - queryMempool: queryMempool - }, function(err, outputs) { + this.getOutputs(address, {queryMempool: queryMempool}, function(err, outputs) { if (err) { return callback(err); - } else if (!outputs.length) { + } else if(!outputs.length) { return callback(new errors.NoOutputs('Address ' + address + ' has no outputs'), []); } @@ -1350,7 +1352,6 @@ AddressService.prototype.getAddressSummary = function(addressArg, options, callb } async.waterfall([ - function(next) { self._getAddressConfirmedSummary(address, options, next); }, @@ -1372,7 +1373,9 @@ AddressService.prototype.getAddressSummary = function(addressArg, options, callb var seconds = Math.round(timeDelta / 1000); log.warn('Slow (' + seconds + 's) getAddressSummary request for address: ' + address.toString()); } + callback(null, summary); + }); }; @@ -1388,7 +1391,6 @@ AddressService.prototype._getAddressConfirmedSummary = function(address, options }; async.waterfall([ - function(next) { self._getAddressConfirmedInputsSummary(address, baseResult, options, next); }, @@ -1435,8 +1437,8 @@ AddressService.prototype._getAddressConfirmedInputsSummary = function(address, r AddressService.prototype._getAddressConfirmedOutputsSummary = function(address, result, options, callback) { $.checkArgument(address instanceof Address); $.checkArgument(!_.isUndefined(result) && - !_.isUndefined(result.appearanceIds) && - !_.isUndefined(result.unconfirmedAppearanceIds)); + !_.isUndefined(result.appearanceIds) && + !_.isUndefined(result.unconfirmedAppearanceIds)); var self = this; var count = 0; @@ -1450,16 +1452,16 @@ AddressService.prototype._getAddressConfirmedOutputsSummary = function(address, result.totalReceived += output.satoshis; result.appearanceIds[txid] = output.height; - if (!options.noBalance) { + if(!options.noBalance) { // Bitcoind's isSpent only works for confirmed transactions var spentDB = self.node.services.bitcoind.isSpent(txid, outputIndex); - if (!spentDB) { + if(!spentDB) { result.balance += output.satoshis; } - if (options.queryMempool) { + if(options.queryMempool) { // Check to see if this output is spent in the mempool and if so // we will subtract it from the unconfirmedBalance (a.k.a unconfirmedDelta) var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( @@ -1467,7 +1469,7 @@ AddressService.prototype._getAddressConfirmedOutputsSummary = function(address, outputIndex ); var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; - if (spentMempool) { + if(spentMempool) { result.unconfirmedBalance -= output.satoshis; } } @@ -1523,43 +1525,41 @@ AddressService.prototype._getAddressMempoolSummary = function(address, options, var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type]; var hashBufferHex = hashBuffer.toString('hex'); - if (!this.mempoolAddressIndex[hashBufferHex]) { + if(!this.mempoolAddressIndex[hashBufferHex]) { return callback(null, result); } async.waterfall([ - function(next) { self._getInputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolInputs) { if (err) { return next(err); } - for (var i = 0; i < mempoolInputs.length; i++) { + for(var i = 0; i < mempoolInputs.length; i++) { var input = mempoolInputs[i]; result.unconfirmedAppearanceIds[input.txid] = input.timestamp; } next(null, result); }); - }, - function(result, next) { + }, function(result, next) { self._getOutputsMempool(addressStr, hashBuffer, hashTypeBuffer, function(err, mempoolOutputs) { if (err) { return next(err); } - for (var i = 0; i < mempoolOutputs.length; i++) { + for(var i = 0; i < mempoolOutputs.length; i++) { var output = mempoolOutputs[i]; result.unconfirmedAppearanceIds[output.txid] = output.timestamp; - if (!options.noBalance) { + if(!options.noBalance) { var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey( new Buffer(output.txid, 'hex'), // TODO: get buffer directly output.outputIndex ); var spentMempool = self.mempoolSpentIndex[spentIndexSyncKey]; // Only add this to the balance if it's not spent in the mempool already - if (!spentMempool) { + if(!spentMempool) { result.unconfirmedBalance += output.satoshis; } } diff --git a/test/services/address/history.unit.js b/test/services/address/history.unit.js index 917ac735..ab3ee5c8 100644 --- a/test/services/address/history.unit.js +++ b/test/services/address/history.unit.js @@ -122,7 +122,9 @@ describe('Address Service History', function() { history.get(function() { history.node.services.address.getAddressSummary.callCount.should.equal(1); history.node.services.address.getAddressSummary.args[0][0].should.equal(address); - history.node.services.address.getAddressSummary.args[0][1].should.equal(options); + history.node.services.address.getAddressSummary.args[0][1].should.equal({ + noBalance: true, + }); history._paginateWithDetails.callCount.should.equal(1); history._paginateWithDetails.args[0][0].should.equal(txids); history._mergeAndSortTxids.callCount.should.equal(0); @@ -155,7 +157,6 @@ describe('Address Service History', function() { history.node.services.address.getAddressSummary.args[0][0].should.equal(address); history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({ fullTxList: true, - noBalance: true, }); history._paginateWithDetails.callCount.should.equal(1); history._paginateWithDetails.args[0][0].should.equal(txids); From d0c2fa61d8526d23f102778f8da472f0139b18a8 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 9 Feb 2016 10:57:40 -0500 Subject: [PATCH 06/13] fix tests --- lib/services/address/encoding.js | 9 +++++++++ lib/services/address/index.js | 17 +++++++++-------- test/services/address/history.unit.js | 3 ++- test/services/address/index.unit.js | 5 +++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/services/address/encoding.js b/lib/services/address/encoding.js index 81421a18..ad4b55eb 100644 --- a/lib/services/address/encoding.js +++ b/lib/services/address/encoding.js @@ -19,6 +19,15 @@ exports.encodeSpentIndexSyncKey = function(txidBuffer, outputIndex) { return key.toString('binary'); }; +exports.encodeMempoolAddressIndexKey = function(hashBuffer, hashTypeBuffer) { + var key = Buffer.concat([ + hashBuffer, + hashTypeBuffer, + ]); + return key; +}; + + exports.encodeOutputKey = function(hashBuffer, hashTypeBuffer, height, txidBuffer, outputIndex) { var heightBuffer = new Buffer(4); heightBuffer.writeUInt32BE(height); diff --git a/lib/services/address/index.js b/lib/services/address/index.js index da903766..b5a98e18 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -307,12 +307,12 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { continue; } - var hashBufferHex = addressInfo.hashBuffer.toString('hex'); + var addressIndexKey = encoding.encodeMempoolAddressIndexKey(addressInfo.hashBuffer, addressInfo.hashTypeBuffer); if(add) { - this.mempoolAddressIndex[hashBufferHex] = true; + this.mempoolAddressIndex[addressIndexKey] = true; } else { - delete this.mempoolAddressIndex[hashBufferHex]; + delete this.mempoolAddressIndex[addressIndexKey]; } // Update output index @@ -407,11 +407,12 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { value: inputValue }); - var inputHashBufferHex = inputHashBuffer.toString('hex'); + var addressIndexKey = encoding.encodeMempoolAddressIndexKey(inputHashBuffer, inputHashType); + if(add) { - this.mempoolAddressIndex[inputHashBufferHex] = true; + this.mempoolAddressIndex[addressIndexKey] = true; } else { - delete this.mempoolAddressIndex[inputHashBufferHex]; + delete this.mempoolAddressIndex[addressIndexKey]; } } @@ -1523,9 +1524,9 @@ AddressService.prototype._getAddressMempoolSummary = function(address, options, var addressStr = address.toString(); var hashBuffer = address.hashBuffer; var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type]; - var hashBufferHex = hashBuffer.toString('hex'); + var addressIndexKey = encoding.encodeMempoolAddressIndexKey(hashBuffer, hashTypeBuffer); - if(!this.mempoolAddressIndex[hashBufferHex]) { + if(!this.mempoolAddressIndex[addressIndexKey]) { return callback(null, result); } diff --git a/test/services/address/history.unit.js b/test/services/address/history.unit.js index ab3ee5c8..4c6306dd 100644 --- a/test/services/address/history.unit.js +++ b/test/services/address/history.unit.js @@ -122,7 +122,7 @@ describe('Address Service History', function() { history.get(function() { history.node.services.address.getAddressSummary.callCount.should.equal(1); history.node.services.address.getAddressSummary.args[0][0].should.equal(address); - history.node.services.address.getAddressSummary.args[0][1].should.equal({ + history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({ noBalance: true, }); history._paginateWithDetails.callCount.should.equal(1); @@ -157,6 +157,7 @@ describe('Address Service History', function() { history.node.services.address.getAddressSummary.args[0][0].should.equal(address); history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({ fullTxList: true, + noBalance: true, }); history._paginateWithDetails.callCount.should.equal(1); history._paginateWithDetails.args[0][0].should.equal(txids); diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 4d4061e1..62745395 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -2521,8 +2521,9 @@ describe('Address Service', function() { ); as.mempoolSpentIndex[spentIndexSyncKey] = true; - var hashBufferHex = address.hashBuffer.toString('hex'); - as.mempoolAddressIndex[hashBufferHex] = true; + var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type]; + var addressIndex = encoding.encodeMempoolAddressIndexKey(address.hashBuffer, hashTypeBuffer); + as.mempoolAddressIndex[addressIndex] = true; as._getInputsMempool = sinon.stub().callsArgWith(3, null, mempoolInputs); as._getOutputsMempool = sinon.stub().callsArgWith(3, null, mempoolOutputs); From e7895b4b34001c25ec71be4f37abb076ce230618 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 9 Feb 2016 15:30:40 -0500 Subject: [PATCH 07/13] use key as binary --- lib/services/address/encoding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/address/encoding.js b/lib/services/address/encoding.js index ad4b55eb..8ebce51c 100644 --- a/lib/services/address/encoding.js +++ b/lib/services/address/encoding.js @@ -24,7 +24,7 @@ exports.encodeMempoolAddressIndexKey = function(hashBuffer, hashTypeBuffer) { hashBuffer, hashTypeBuffer, ]); - return key; + return key.toString('binary'); }; From 3bb3d82aaca05c78b1d389f1d03fb8b79a0e32dd Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 10 Feb 2016 15:03:34 -0500 Subject: [PATCH 08/13] add counter for address mempool index --- lib/services/address/index.js | 31 +++++++++++++++++++---------- test/services/address/index.unit.js | 5 ++++- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index b5a98e18..35829758 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -275,6 +275,25 @@ AddressService.prototype.transactionHandler = function(txInfo, callback) { }; +AddressService.prototype._updateAddressIndex = function(key, add) { + + var currentValue = this.mempoolAddressIndex[key] || 0; + + if(add) { + if (currentValue > 0) + this.mempoolAddressIndex[key] = currentValue + 1; + else + this.mempoolAddressIndex[key] = 1; + } else { + if (currentValue < 1) { + delete this.mempoolAddressIndex[key]; + } else { + this.mempoolAddressIndex[key]--; + } + } +}; + + /** * This function will update the mempool address index with the necessary * information for further lookups. @@ -309,11 +328,7 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { var addressIndexKey = encoding.encodeMempoolAddressIndexKey(addressInfo.hashBuffer, addressInfo.hashTypeBuffer); - if(add) { - this.mempoolAddressIndex[addressIndexKey] = true; - } else { - delete this.mempoolAddressIndex[addressIndexKey]; - } + this._updateAddressIndex(addressIndexKey, add); // Update output index var outputIndexBuffer = new Buffer(4); @@ -409,11 +424,7 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) { var addressIndexKey = encoding.encodeMempoolAddressIndexKey(inputHashBuffer, inputHashType); - if(add) { - this.mempoolAddressIndex[addressIndexKey] = true; - } else { - delete this.mempoolAddressIndex[addressIndexKey]; - } + this._updateAddressIndex(addressIndexKey, add); } if (!callback) { diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 62745395..d5632c8f 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -9,6 +9,7 @@ var bitcorenode = require('../../../'); var AddressService = bitcorenode.services.Address; var blockData = require('../../data/livenet-345003.json'); var bitcore = require('bitcore-lib'); +var _ = bitcore.deps._; var memdown = require('memdown'); var leveldown = require('leveldown'); var Networks = bitcore.Networks; @@ -1978,6 +1979,7 @@ describe('Address Service', function() { callback.should.be.a('function'); Object.keys(am.mempoolSpentIndex).length.should.equal(14); Object.keys(am.mempoolAddressIndex).length.should.equal(5); + _.values(am.mempoolAddressIndex).should.deep.equal([1,1,12,1,1]); for (var i = 0; i < operations.length; i++) { operations[i].type.should.equal('put'); } @@ -2013,6 +2015,7 @@ describe('Address Service', function() { for (var i = 0; i < operations.length; i++) { operations[i].type.should.equal('del'); } + Object.keys(am.mempoolAddressIndex).length.should.equal(0); }; am.updateMempoolIndex(tx, false); }); @@ -2523,7 +2526,7 @@ describe('Address Service', function() { var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type]; var addressIndex = encoding.encodeMempoolAddressIndexKey(address.hashBuffer, hashTypeBuffer); - as.mempoolAddressIndex[addressIndex] = true; + as.mempoolAddressIndex[addressIndex] = 1; as._getInputsMempool = sinon.stub().callsArgWith(3, null, mempoolInputs); as._getOutputsMempool = sinon.stub().callsArgWith(3, null, mempoolOutputs); From 02f2234004fdc41092b090fd03e18d14bafd0466 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 10 Feb 2016 15:05:05 -0500 Subject: [PATCH 09/13] rm extra commas --- test/services/address/history.unit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/services/address/history.unit.js b/test/services/address/history.unit.js index 4c6306dd..2b6df06c 100644 --- a/test/services/address/history.unit.js +++ b/test/services/address/history.unit.js @@ -123,7 +123,7 @@ describe('Address Service History', function() { history.node.services.address.getAddressSummary.callCount.should.equal(1); history.node.services.address.getAddressSummary.args[0][0].should.equal(address); history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({ - noBalance: true, + noBalance: true }); history._paginateWithDetails.callCount.should.equal(1); history._paginateWithDetails.args[0][0].should.equal(txids); @@ -157,7 +157,7 @@ describe('Address Service History', function() { history.node.services.address.getAddressSummary.args[0][0].should.equal(address); history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({ fullTxList: true, - noBalance: true, + noBalance: true }); history._paginateWithDetails.callCount.should.equal(1); history._paginateWithDetails.args[0][0].should.equal(txids); From 1a68ca4fae32b6255cd8af6fa9d2cdcff9e4c9bf Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 10 Feb 2016 15:38:02 -0500 Subject: [PATCH 10/13] add tests to _updateAddressIndex --- lib/services/address/index.js | 6 ++-- test/services/address/index.unit.js | 50 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 35829758..bdbfe342 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -276,14 +276,14 @@ AddressService.prototype.transactionHandler = function(txInfo, callback) { }; AddressService.prototype._updateAddressIndex = function(key, add) { - var currentValue = this.mempoolAddressIndex[key] || 0; if(add) { - if (currentValue > 0) + if (currentValue > 0) { this.mempoolAddressIndex[key] = currentValue + 1; - else + } else { this.mempoolAddressIndex[key] = 1; + } } else { if (currentValue < 1) { delete this.mempoolAddressIndex[key]; diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index d5632c8f..3c2b250b 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -2433,6 +2433,56 @@ describe('Address Service', function() { }); }); + + describe.only('#_updateAddressIndex', function() { + var as; + beforeEach(function(){ + var testnode = { + services: { + bitcoind: { + on: sinon.stub() + } + }, + datadir: 'testdir' + }; + as = new AddressService({ + mempoolMemoryIndex: true, + node: testnode + }); + }); + it('should add using 2 keys', function() { + _.values(as.mempoolAddressIndex).should.deep.equal([]); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index2', true); + as._updateAddressIndex('index2', true); + as.mempoolAddressIndex.should.deep.equal({ + "index1": 4, + "index2": 2 + }); + }); + it('should add/remove using 2 keys', function() { + _.values(as.mempoolAddressIndex).should.deep.equal([]); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', true); + as._updateAddressIndex('index1', false); + + as._updateAddressIndex('index2', true); + as._updateAddressIndex('index2', true); + as._updateAddressIndex('index2', false); + as._updateAddressIndex('index2', false); + as._updateAddressIndex('index2', false); + as.mempoolAddressIndex.should.deep.equal({ + "index1": 3 + }); + }); + }); + + describe('#_getAddressMempoolSummary', function() { it('skip if options not enabled', function(done) { var testnode = { From 6e600b5def5ca1eff68c9aea7f2ae23dfcbc0a28 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 10 Feb 2016 16:08:27 -0500 Subject: [PATCH 11/13] refactor test --- test/services/address/index.unit.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 3c2b250b..437c8673 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -2434,9 +2434,8 @@ describe('Address Service', function() { }); - describe.only('#_updateAddressIndex', function() { - var as; - beforeEach(function(){ + describe('#_updateAddressIndex', function() { + it('should add using 2 keys', function() { var testnode = { services: { bitcoind: { @@ -2445,12 +2444,11 @@ describe('Address Service', function() { }, datadir: 'testdir' }; - as = new AddressService({ + var as = new AddressService({ mempoolMemoryIndex: true, node: testnode }); - }); - it('should add using 2 keys', function() { + _.values(as.mempoolAddressIndex).should.deep.equal([]); as._updateAddressIndex('index1', true); as._updateAddressIndex('index1', true); @@ -2463,7 +2461,20 @@ describe('Address Service', function() { "index2": 2 }); }); + it('should add/remove using 2 keys', function() { + var testnode = { + services: { + bitcoind: { + on: sinon.stub() + } + }, + datadir: 'testdir' + }; + var as = new AddressService({ + mempoolMemoryIndex: true, + node: testnode + }); _.values(as.mempoolAddressIndex).should.deep.equal([]); as._updateAddressIndex('index1', true); as._updateAddressIndex('index1', true); From 4d03aaa73fbc389f01079f723ab9e88a250bd53a Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 10 Feb 2016 16:18:27 -0500 Subject: [PATCH 12/13] use mocknode --- test/services/address/index.unit.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 437c8673..8e8527f4 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -2436,17 +2436,9 @@ describe('Address Service', function() { describe('#_updateAddressIndex', function() { it('should add using 2 keys', function() { - var testnode = { - services: { - bitcoind: { - on: sinon.stub() - } - }, - datadir: 'testdir' - }; var as = new AddressService({ mempoolMemoryIndex: true, - node: testnode + node: mocknode }); _.values(as.mempoolAddressIndex).should.deep.equal([]); @@ -2463,17 +2455,9 @@ describe('Address Service', function() { }); it('should add/remove using 2 keys', function() { - var testnode = { - services: { - bitcoind: { - on: sinon.stub() - } - }, - datadir: 'testdir' - }; var as = new AddressService({ mempoolMemoryIndex: true, - node: testnode + node: mocknode }); _.values(as.mempoolAddressIndex).should.deep.equal([]); as._updateAddressIndex('index1', true); From e36cdb717a47e10c7da5164be7df9555e3bf034f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 11 Feb 2016 10:42:30 -0500 Subject: [PATCH 13/13] rm empty keys --- lib/services/address/index.js | 2 +- test/services/address/index.unit.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/services/address/index.js b/lib/services/address/index.js index bdbfe342..0a3c3555 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -285,7 +285,7 @@ AddressService.prototype._updateAddressIndex = function(key, add) { this.mempoolAddressIndex[key] = 1; } } else { - if (currentValue < 1) { + if (currentValue <= 1) { delete this.mempoolAddressIndex[key]; } else { this.mempoolAddressIndex[key]--; diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 8e8527f4..ce971636 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -2470,6 +2470,9 @@ describe('Address Service', function() { as._updateAddressIndex('index2', true); as._updateAddressIndex('index2', false); as._updateAddressIndex('index2', false); + as.mempoolAddressIndex.should.deep.equal({ + "index1": 3 + }); as._updateAddressIndex('index2', false); as.mempoolAddressIndex.should.deep.equal({ "index1": 3