diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 12eda69a..82eb4527 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -9,6 +9,7 @@ var errors = index.errors; var bitcore = require('bitcore'); var $ = bitcore.util.preconditions; var _ = bitcore.deps._; +var Hash = bitcore.crypto.Hash; var EventEmitter = require('events').EventEmitter; var PublicKey = bitcore.PublicKey; var Address = bitcore.Address; @@ -81,23 +82,21 @@ AddressService.prototype.transactionOutputHandler = function(messages, tx, outpu return; } - // Find the address for the output - var address = script.toAddress(this.node.network); - if (!address && script.isPublicKeyOut()) { - var pubkey = script.chunks[0].buf; - address = Address.fromPublicKey(new PublicKey(pubkey), this.node.network); - } else if (!address){ + var addressInfo = this._extractAddressInfoFromScript(script); + if (!addressInfo) { return; } + addressInfo.hashHex = addressInfo.hashBuffer.toString('hex'); + // Collect data to publish to address subscribers - if (messages[address]) { - messages[address].outputIndexes.push(outputIndex); + if (messages[addressInfo.hashHex]) { + messages[addressInfo.hashHex].outputIndexes.push(outputIndex); } else { - messages[address] = { + messages[addressInfo.hashHex] = { tx: tx, outputIndexes: [outputIndex], - address: address.toString(), + addressInfo: addressInfo, rejected: rejected }; } @@ -130,6 +129,28 @@ AddressService.prototype.transactionHandler = function(txInfo) { } }; +AddressService.prototype._extractAddressInfoFromScript = function(script) { + var hashBuffer; + var addressType; + if (script.isPublicKeyHashOut()) { + hashBuffer = script.chunks[2].buf; + addressType = Address.PayToPublicKeyHash; + } else if (script.isScriptHashOut()) { + hashBuffer = script.chunks[1].buf; + addressType = Address.PayToScriptHash; + } else if (script.isPublicKeyOut()) { + var pubkey = script.chunks[0].buf; + var address = Address.fromPublicKey(new PublicKey(pubkey), this.node.network); + hashBuffer = address.hashBuffer; + } else { + return false; + } + return { + hashBuffer: hashBuffer, + addressType: addressType + }; +}; + AddressService.prototype.blockHandler = function(block, addOutput, callback) { var txs = block.transactions; @@ -162,14 +183,13 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { continue; } - var address = script.toAddress(this.node.network); - if (!address && script.isPublicKeyOut()) { - var pubkey = script.chunks[0].buf; - address = Address.fromPublicKey(new PublicKey(pubkey), this.node.network); - } else if (!address){ + var addressInfo = this._extractAddressInfoFromScript(script); + if (!addressInfo) { continue; } + addressInfo.hashHex = addressInfo.hashBuffer.toString('hex'); + // We need to use the height for indexes (and not the timestamp) because the // the timestamp has unreliable sequential ordering. The next block // can have a time that is previous to the previous block (however not @@ -177,18 +197,19 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { // hours in the future. var height = block.__height; - var addressStr = address.toString(); var scriptHex = output._scriptBuffer.toString('hex'); // To lookup outputs by address and height var key = [ AddressService.PREFIXES.OUTPUTS, - addressStr, + addressInfo.hashHex, height, txid, outputIndex ].join('-'); + // TODO use buffers directly to save on disk storage + var value = [output.satoshis, scriptHex].join(':'); operations.push({ @@ -198,19 +219,19 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { }); // Collect data for subscribers - if (txmessages[addressStr]) { - txmessages[addressStr].outputIndexes.push(outputIndex); + if (txmessages[addressInfo.hashHex]) { + txmessages[addressInfo.hashHex].outputIndexes.push(outputIndex); } else { - txmessages[addressStr] = { + txmessages[addressInfo.hashHex] = { tx: tx, height: block.__height, outputIndexes: [outputIndex], - address: addressStr, + addressInfo: addressInfo, timestamp: block.header.timestamp }; } - this.balanceEventHandler(block, address); + this.balanceEventHandler(block, addressInfo); } @@ -226,35 +247,36 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { for(var inputIndex = 0; inputIndex < inputs.length; inputIndex++) { var input = inputs[inputIndex]; - var inputAddress = input.script.toAddress(this.node.network); - if (inputAddress) { + var inputHashBuffer; - var inputObject = input.toObject(); - var inputAddressStr = inputAddress.toString(); - - var height = block.__height; - - // To be able to query inputs by address and spent height - var inputKey = [ - AddressService.PREFIXES.SPENTS, - inputAddressStr, - height, - inputObject.prevTxId, - inputObject.outputIndex - ].join('-'); - - var inputValue = [ - txid, - inputIndex - ].join(':'); - - operations.push({ - type: action, - key: inputKey, - value: inputValue - }); + if (input.script.isPublicKeyHashIn()) { + inputHashBuffer = Hash.sha256ripemd160(input.script.chunks[1].buf); + } else if (input.script.isScriptHashIn()) { + inputHashBuffer = Hash.sha256ripemd160(input.script.chunks[input.script.chunks.length - 1].buf); + } else { + continue; } + + // To be able to query inputs by address and spent height + var inputKey = [ + AddressService.PREFIXES.SPENTS, + inputHashBuffer.toString('hex'), + block.__height, + input.prevTxId.toString('hex'), + input.outputIndex + ].join('-'); + + var inputValue = [ + txid, + inputIndex + ].join(':'); + + operations.push({ + type: action, + key: inputKey, + value: inputValue + }); } } @@ -266,30 +288,57 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) { /** * @param {Object} obj * @param {Transaction} obj.tx - The transaction - * @param {String} [obj.address] - The address for the subscription - * @param {Array} [obj.outputIndexes] - Indexes of the inputs that includes the address - * @param {Array} [obj.inputIndexes] - Indexes of the outputs that includes the address - * @param {Date} [obj.timestamp] - The time of the block the transaction was included - * @param {Number} [obj.height] - The height of the block the transaction was included - * @param {Boolean} [obj.rejected] - If the transaction was not accepted in the mempool + * @param {Object} obj.addressInfo + * @param {String} obj.addressInfo.hashHex - The hex string of address hash for the subscription + * @param {String} obj.addressInfo.hashBuffer - The address hash buffer + * @param {String} obj.addressInfo.addressType - The address type + * @param {Array} obj.outputIndexes - Indexes of the inputs that includes the address + * @param {Array} obj.inputIndexes - Indexes of the outputs that includes the address + * @param {Date} obj.timestamp - The time of the block the transaction was included + * @param {Number} obj.height - The height of the block the transaction was included + * @param {Boolean} obj.rejected - If the transaction was not accepted in the mempool */ AddressService.prototype.transactionEventHandler = function(obj) { - if(this.subscriptions['address/transaction'][obj.address]) { - var emitters = this.subscriptions['address/transaction'][obj.address]; + 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++) { - emitters[i].emit('address/transaction', obj); + emitters[i].emit('address/transaction', { + rejected: obj.rejected, + height: obj.height, + timestamp: obj.timestamp, + inputIndexes: obj.inputIndexes, + outputIndexes: obj.outputIndexes, + address: address, + tx: obj.tx + }); } } }; -AddressService.prototype.balanceEventHandler = function(block, address) { - if(this.subscriptions['address/balance'][address]) { - var emitters = this.subscriptions['address/balance'][address]; +/** + * @param {Block} block + * @param {Object} obj + * @param {String} obj.hashHex + * @param {Buffer} obj.hashBuffer + * @param {String} obj.addressType + */ +AddressService.prototype.balanceEventHandler = function(block, obj) { + if(this.subscriptions['address/balance'][obj.hashHex]) { + var emitters = this.subscriptions['address/balance'][obj.hashHex]; + var address = new Address({ + hashBuffer: obj.hashBuffer, + network: this.node.network, + type: obj.addressType + }); this.getBalance(address, true, function(err, balance) { if(err) { return this.emit(err); } - for(var i = 0; i < emitters.length; i++) { emitters[i].emit('address/balance', address, balance, block); } @@ -302,10 +351,11 @@ AddressService.prototype.subscribe = function(name, emitter, addresses) { $.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses'); for(var i = 0; i < addresses.length; i++) { - if(!this.subscriptions[name][addresses[i]]) { - this.subscriptions[name][addresses[i]] = []; + var hashHex = bitcore.Address(addresses[i]).hashBuffer.toString('hex'); + if(!this.subscriptions[name][hashHex]) { + this.subscriptions[name][hashHex] = []; } - this.subscriptions[name][addresses[i]].push(emitter); + this.subscriptions[name][hashHex].push(emitter); } }; @@ -318,8 +368,9 @@ AddressService.prototype.unsubscribe = function(name, emitter, addresses) { } for(var i = 0; i < addresses.length; i++) { - if(this.subscriptions[name][addresses[i]]) { - var emitters = this.subscriptions[name][addresses[i]]; + var hashHex = bitcore.Address(addresses[i]).hashBuffer.toString('hex'); + if(this.subscriptions[name][hashHex]) { + var emitters = this.subscriptions[name][hashHex]; var index = emitters.indexOf(emitter); if(index > -1) { emitters.splice(index, 1); @@ -331,8 +382,8 @@ 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 address in this.subscriptions[name]) { - var emitters = this.subscriptions[name][address]; + for(var hashHex in this.subscriptions[name]) { + var emitters = this.subscriptions[name][hashHex]; var index = emitters.indexOf(emitter); if(index > -1) { emitters.splice(index, 1); @@ -373,21 +424,23 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) { var inputs = []; var stream; + var hashHex = bitcore.Address(addressStr).hashBuffer.toString('hex'); + if (options.start && options.end) { // The positions will be flipped because the end position should be greater // than the starting position for the stream, and we'll add one to the end key // so that it's included in the results. - var endKey = [AddressService.PREFIXES.SPENTS, addressStr, options.start + 1].join('-'); - var startKey = [AddressService.PREFIXES.SPENTS, addressStr, options.end].join('-'); + var endKey = [AddressService.PREFIXES.SPENTS, hashHex, options.start + 1].join('-'); + var startKey = [AddressService.PREFIXES.SPENTS, hashHex, options.end].join('-'); stream = this.node.services.db.store.createReadStream({ start: startKey, end: endKey }); } else { - var allKey = [AddressService.PREFIXES.SPENTS, addressStr].join('-'); + var allKey = [AddressService.PREFIXES.SPENTS, hashHex].join('-'); stream = this.node.services.db.store.createReadStream({ start: allKey, end: allKey + '~' @@ -449,6 +502,8 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) { $.checkArgument(_.isObject(options), 'Second argument is expected to be an options object.'); $.checkArgument(_.isFunction(callback), 'Third argument is expected to be a callback function.'); + var hashHex = bitcore.Address(addressStr).hashBuffer.toString('hex'); + var outputs = []; var stream; @@ -457,15 +512,15 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) { // The positions will be flipped because the end position should be greater // than the starting position for the stream, and we'll add one to the end key // so that it's included in the results. - var endKey = [AddressService.PREFIXES.OUTPUTS, addressStr, options.start + 1].join('-'); - var startKey = [AddressService.PREFIXES.OUTPUTS, addressStr, options.end].join('-'); + var endKey = [AddressService.PREFIXES.OUTPUTS, hashHex, options.start + 1].join('-'); + var startKey = [AddressService.PREFIXES.OUTPUTS, hashHex, options.end].join('-'); stream = this.node.services.db.store.createReadStream({ start: startKey, end: endKey }); } else { - var allKey = [AddressService.PREFIXES.OUTPUTS, addressStr].join('-'); + var allKey = [AddressService.PREFIXES.OUTPUTS, hashHex].join('-'); stream = this.node.services.db.store.createReadStream({ start: allKey, end: allKey + '~' diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 79c31287..468a75a9 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -74,13 +74,15 @@ describe('Address Service', function() { var am = new AddressService({node: mocknode}); am.node.network = Networks.livenet; var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX'; + var hashHex = bitcore.Address(address).hashBuffer.toString('hex'); var messages = {}; am.transactionOutputHandler(messages, tx, 0, true); - should.exist(messages[address]); - var message = messages[address]; + should.exist(messages[hashHex]); + var message = messages[hashHex]; message.tx.should.equal(tx); message.outputIndexes.should.deep.equal([0]); - message.address.should.equal(address); + message.addressInfo.hashBuffer.toString('hex').should.equal(hashHex); + message.addressInfo.hashHex.should.equal(hashHex); message.rejected.should.equal(true); }); }); @@ -109,7 +111,7 @@ describe('Address Service', function() { var data = [ { key: { - address: '1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw', + hashHex: bitcore.Address('1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw').hashBuffer.toString('hex'), height: 345003, txid: 'fdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e16923', outputIndex: 0 @@ -122,7 +124,7 @@ describe('Address Service', function() { }, { key: { - address: '1Q8ec8kG7c7HqgK7uSzQyWsX9tzepRcKEL', + hashHex: bitcore.Address('1Q8ec8kG7c7HqgK7uSzQyWsX9tzepRcKEL').hashBuffer.toString('hex'), height: 345003, prevTxId: '3d7d5d98df753ef2a4f82438513c509e3b11f3e738e94a7234967b03a03123a9', prevOutputIndex: 32 @@ -134,7 +136,7 @@ describe('Address Service', function() { }, { key: { - address: '1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm', + hashHex: bitcore.Address('1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm').hashBuffer.toString('hex'), height: 345003, txid: 'e66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d', outputIndex: 1 @@ -171,15 +173,15 @@ describe('Address Service', function() { should.not.exist(err); operations.length.should.equal(81); operations[0].type.should.equal('put'); - var expected0 = ['outs', key0.address, key0.height, key0.txid, key0.outputIndex].join('-'); + var expected0 = ['outs', key0.hashHex, key0.height, key0.txid, key0.outputIndex].join('-'); operations[0].key.should.equal(expected0); operations[0].value.should.equal([value0.satoshis, value0.script].join(':')); operations[3].type.should.equal('put'); - var expected3 = ['sp', key3.address, key3.height, key3.prevTxId, key3.prevOutputIndex].join('-'); + var expected3 = ['sp', key3.hashHex, key3.height, key3.prevTxId, key3.prevOutputIndex].join('-'); operations[3].key.should.equal(expected3); operations[3].value.should.equal([value3.txid, value3.inputIndex].join(':')); operations[64].type.should.equal('put'); - var expected64 = ['outs', key64.address, key64.height, key64.txid, key64.outputIndex].join('-'); + var expected64 = ['outs', key64.hashHex, key64.height, key64.txid, key64.outputIndex].join('-'); operations[64].key.should.equal(expected64); operations[64].value.should.equal([value64.satoshis, value64.script].join(':')); done(); @@ -197,13 +199,13 @@ describe('Address Service', function() { should.not.exist(err); operations.length.should.equal(81); operations[0].type.should.equal('del'); - operations[0].key.should.equal(['outs', key0.address, key0.height, key0.txid, key0.outputIndex].join('-')); + operations[0].key.should.equal(['outs', key0.hashHex, key0.height, key0.txid, key0.outputIndex].join('-')); operations[0].value.should.equal([value0.satoshis, value0.script].join(':')); operations[3].type.should.equal('del'); - operations[3].key.should.equal(['sp', key3.address, key3.height, key3.prevTxId, key3.prevOutputIndex].join('-')); + operations[3].key.should.equal(['sp', key3.hashHex, key3.height, key3.prevTxId, key3.prevOutputIndex].join('-')); operations[3].value.should.equal([value3.txid, value3.inputIndex].join(':')); operations[64].type.should.equal('del'); - operations[64].key.should.equal(['outs', key64.address, key64.height, key64.txid, key64.outputIndex].join('-')); + operations[64].key.should.equal(['outs', key64.hashHex, key64.height, key64.txid, key64.outputIndex].join('-')); operations[64].value.should.equal([value64.satoshis, value64.script].join(':')); done(); }); @@ -277,16 +279,16 @@ describe('Address Service', function() { it('will emit a transaction if there is a subscriber', function(done) { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); - am.subscriptions['address/transaction'] = { - '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter] - }; + var address = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + am.subscriptions['address/transaction'] = {}; + am.subscriptions['address/transaction'][address.hashBuffer.toString('hex')] = [emitter]; var block = { __height: 0, timestamp: new Date() }; var tx = {}; emitter.on('address/transaction', function(obj) { - obj.address.should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + obj.address.toString().should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); obj.tx.should.equal(tx); obj.timestamp.should.equal(block.timestamp); obj.height.should.equal(block.__height); @@ -294,7 +296,11 @@ describe('Address Service', function() { done(); }); am.transactionEventHandler({ - address: '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N', + addressInfo: { + hashHex: address.hashBuffer.toString('hex'), + hashBuffer: address.hashBuffer, + addressType: address.type + }, height: block.__height, timestamp: block.timestamp, outputIndexes: [1], @@ -307,19 +313,22 @@ describe('Address Service', function() { it('will emit a balance if there is a subscriber', function(done) { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); - am.subscriptions['address/balance'] = { - '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter] - }; + var address = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + am.subscriptions['address/balance'][address.hashBuffer.toString('hex')] = [emitter]; var block = {}; var balance = 1000; am.getBalance = sinon.stub().callsArgWith(2, null, balance); - emitter.on('address/balance', function(address, bal, b) { - address.should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + emitter.on('address/balance', function(a, bal, b) { + a.toString().should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); bal.should.equal(balance); b.should.equal(block); done(); }); - am.balanceEventHandler(block, '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + am.balanceEventHandler(block, { + hashHex: address.hashBuffer.toString('hex'), + hashBuffer: address.hashBuffer, + addressType: address.type + }); }); }); @@ -328,34 +337,40 @@ describe('Address Service', function() { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); - var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; + var address = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); var name = 'address/transaction'; am.subscribe(name, emitter, [address]); - am.subscriptions['address/transaction'][address].should.deep.equal([emitter]); + am.subscriptions['address/transaction'][address.hashBuffer.toString('hex')] + .should.deep.equal([emitter]); - var address2 = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'; + var address2 = bitcore.Address('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'); am.subscribe(name, emitter, [address2]); - am.subscriptions['address/transaction'][address2].should.deep.equal([emitter]); + am.subscriptions['address/transaction'][address2.hashBuffer.toString('hex')] + .should.deep.equal([emitter]); var emitter2 = new EventEmitter(); am.subscribe(name, emitter2, [address]); - am.subscriptions['address/transaction'][address].should.deep.equal([emitter, emitter2]); + am.subscriptions['address/transaction'][address.hashBuffer.toString('hex')] + .should.deep.equal([emitter, emitter2]); }); it('will add an emitter to the subscribers array (balance)', function() { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); var name = 'address/balance'; - var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; + var address = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); am.subscribe(name, emitter, [address]); - am.subscriptions['address/balance'][address].should.deep.equal([emitter]); + am.subscriptions['address/balance'][address.hashBuffer.toString('hex')] + .should.deep.equal([emitter]); - var address2 = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'; + var address2 = bitcore.Address('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'); am.subscribe(name, emitter, [address2]); - am.subscriptions['address/balance'][address2].should.deep.equal([emitter]); + am.subscriptions['address/balance'][address2.hashBuffer.toString('hex')] + .should.deep.equal([emitter]); var emitter2 = new EventEmitter(); am.subscribe(name, emitter2, [address]); - am.subscriptions['address/balance'][address].should.deep.equal([emitter, emitter2]); + am.subscriptions['address/balance'][address.hashBuffer.toString('hex')] + .should.deep.equal([emitter, emitter2]); }); }); @@ -364,35 +379,37 @@ describe('Address Service', function() { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); var emitter2 = new EventEmitter(); - var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; - am.subscriptions['address/transaction'][address] = [emitter, emitter2]; + var address = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + am.subscriptions['address/transaction'][address.hashBuffer.toString('hex')] = [emitter, emitter2]; var name = 'address/transaction'; am.unsubscribe(name, emitter, [address]); - am.subscriptions['address/transaction'][address].should.deep.equal([emitter2]); + am.subscriptions['address/transaction'][address.hashBuffer.toString('hex')] + .should.deep.equal([emitter2]); }); it('will remove emitter from subscribers array (balance)', function() { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); var emitter2 = new EventEmitter(); - var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; + var address = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); var name = 'address/balance'; - am.subscriptions['address/balance'][address] = [emitter, emitter2]; + am.subscriptions['address/balance'][address.hashBuffer.toString('hex')] = [emitter, emitter2]; am.unsubscribe(name, emitter, [address]); - am.subscriptions['address/balance'][address].should.deep.equal([emitter2]); + am.subscriptions['address/balance'][address.hashBuffer.toString('hex')] + .should.deep.equal([emitter2]); }); it('should unsubscribe from all addresses if no addresses are specified', function() { var am = new AddressService({node: mocknode}); var emitter = new EventEmitter(); var emitter2 = new EventEmitter(); - am.subscriptions['address/balance'] = { - '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W': [emitter, emitter2], - '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter2, emitter] - }; + var address1 = bitcore.Address('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'); + var hashHex1 = address1.hashBuffer.toString('hex'); + var address2 = bitcore.Address('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'); + var hashHex2 = address2.hashBuffer.toString('hex'); + am.subscriptions['address/balance'][hashHex1] = [emitter, emitter2]; + am.subscriptions['address/balance'][hashHex2] = [emitter2, emitter]; am.unsubscribe('address/balance', emitter); - am.subscriptions['address/balance'].should.deep.equal({ - '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W': [emitter2], - '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter2] - }); + am.subscriptions['address/balance'][hashHex1].should.deep.equal([emitter2]); + am.subscriptions['address/balance'][hashHex2].should.deep.equal([emitter2]); }); }); @@ -425,6 +442,7 @@ describe('Address Service', function() { describe('#getInputs', function() { var am; var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'; + var hashHex = bitcore.Address(address).hashBuffer.toString('hex'); var db = { tip: { __height: 1 @@ -452,8 +470,8 @@ describe('Address Service', function() { var createReadStreamCallCount = 0; am.node.services.db.store = { createReadStream: function(ops) { - ops.start.should.equal([AddressService.PREFIXES.SPENTS, address, 12].join('-')); - ops.end.should.equal([AddressService.PREFIXES.SPENTS, address, 16].join('-')); + ops.start.should.equal([AddressService.PREFIXES.SPENTS, hashHex, 12].join('-')); + ops.end.should.equal([AddressService.PREFIXES.SPENTS, hashHex, 16].join('-')); createReadStreamCallCount++; return testStream; } @@ -486,8 +504,8 @@ describe('Address Service', function() { var createReadStreamCallCount = 0; am.node.services.db.store = { createReadStream: function(ops) { - ops.start.should.equal([AddressService.PREFIXES.SPENTS, address].join('-')); - ops.end.should.equal([AddressService.PREFIXES.SPENTS, address].join('-') + '~'); + ops.start.should.equal([AddressService.PREFIXES.SPENTS, hashHex].join('-')); + ops.end.should.equal([AddressService.PREFIXES.SPENTS, hashHex].join('-') + '~'); createReadStreamCallCount++; return testStream; } @@ -535,6 +553,7 @@ describe('Address Service', function() { describe('#getOutputs', function() { var am; var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'; + var hashHex = bitcore.Address('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W').hashBuffer.toString('hex'); var db = { tip: { __height: 1 @@ -566,8 +585,8 @@ describe('Address Service', function() { var createReadStreamCallCount = 0; am.node.services.db.store = { createReadStream: function(ops) { - ops.start.should.equal([AddressService.PREFIXES.OUTPUTS, address, 12].join('-')); - ops.end.should.equal([AddressService.PREFIXES.OUTPUTS, address, 16].join('-')); + ops.start.should.equal([AddressService.PREFIXES.OUTPUTS, hashHex, 12].join('-')); + ops.end.should.equal([AddressService.PREFIXES.OUTPUTS, hashHex, 16].join('-')); createReadStreamCallCount++; return testStream; }