Address Block Handling Optimizations

- Changes to use ripemd160 hash directly instead of the base58check encoded values
- Speeds block handling performance by ~4 times
This commit is contained in:
Braydon Fuller 2015-09-11 20:58:59 -04:00
parent c205f781a5
commit 1cf34f2dd8
2 changed files with 201 additions and 127 deletions

View File

@ -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 + '~'

View File

@ -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;
}