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:
parent
c205f781a5
commit
1cf34f2dd8
@ -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 + '~'
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user