changes for BWS integration
This commit is contained in:
parent
52cc91e1d7
commit
78bc054bfa
13
bin/start.js
13
bin/start.js
|
@ -5,6 +5,7 @@ var chainlib = require('chainlib');
|
|||
var socketio = require('socket.io');
|
||||
var log = chainlib.log;
|
||||
log.debug = function() {};
|
||||
var utils = require('../lib/utils');
|
||||
|
||||
var configuration = {
|
||||
datadir: process.env.BITCORENODE_DIR || '~/.bitcoin',
|
||||
|
@ -77,11 +78,7 @@ node.on('ready', function() {
|
|||
}
|
||||
|
||||
if(result) {
|
||||
if(result.toJSON) {
|
||||
response.result = result.toJSON();
|
||||
} else {
|
||||
response.result = result;
|
||||
}
|
||||
response.result = utils.expandObject(result);
|
||||
}
|
||||
|
||||
socketCallback(response);
|
||||
|
@ -114,11 +111,7 @@ node.on('ready', function() {
|
|||
var results = [];
|
||||
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
if(arguments[i].toJSON) {
|
||||
results.push(arguments[i].toJSON());
|
||||
} else {
|
||||
results.push(arguments[i]);
|
||||
}
|
||||
results.push(utils.expandObject(arguments[i]));
|
||||
}
|
||||
|
||||
var params = [event.name].concat(results);
|
||||
|
|
|
@ -11,7 +11,7 @@ socket.on('disconnect', function(){
|
|||
|
||||
var message = {
|
||||
method: 'getOutputs',
|
||||
params: ['1HTxCVrXuthad6YW5895K98XmVsdMvvBSw', true]
|
||||
params: ['2NChMRHVCxTPq9KeyvHQUSbfLaQY55Zzzp8', true]
|
||||
};
|
||||
|
||||
socket.send(message, function(response) {
|
||||
|
@ -37,8 +37,13 @@ socket.send(message2, function(response) {
|
|||
console.log(response.result);
|
||||
});
|
||||
|
||||
socket.on('transaction', function(address, block) {
|
||||
console.log(address, block);
|
||||
socket.on('transaction', function(obj) {
|
||||
console.log(JSON.stringify(obj, null, 2));
|
||||
});
|
||||
|
||||
socket.emit('subscribe', 'transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
|
||||
socket.on('address/transaction', function(obj) {
|
||||
console.log(JSON.stringify(obj, null, 2));
|
||||
});
|
||||
|
||||
socket.emit('subscribe', 'transaction');
|
||||
socket.emit('subscribe', 'address/transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
|
|
@ -306,7 +306,8 @@ describe('Daemon Binding Functionality', function() {
|
|||
var outputs = bitcoind.getMempoolOutputs(changeAddress);
|
||||
var expected = [
|
||||
{
|
||||
script: 'OP_DUP OP_HASH160 073b7eae2823efa349e3b9155b8a735526463a0f OP_EQUALVERIFY OP_CHECKSIG',
|
||||
address: 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up',
|
||||
script: '76a914073b7eae2823efa349e3b9155b8a735526463a0f88ac',
|
||||
satoshis: 40000,
|
||||
txid: tx.hash,
|
||||
outputIndex: 1
|
||||
|
|
|
@ -60,10 +60,6 @@ Block.prototype.toObject = function() {
|
|||
};
|
||||
};
|
||||
|
||||
Block.prototype.toJSON = function() {
|
||||
return JSON.stringify(this.toObject());
|
||||
};
|
||||
|
||||
Block.prototype.headerToBufferWriter = function(bw) {
|
||||
/* jshint maxstatements: 20 */
|
||||
|
||||
|
|
59
lib/bus.js
59
lib/bus.js
|
@ -11,44 +11,53 @@ function Bus(params) {
|
|||
util.inherits(Bus, events.EventEmitter);
|
||||
|
||||
Bus.prototype.subscribe = function(name) {
|
||||
for (var i = 0; i < this.db.modules.length; i++) {
|
||||
var events = this.db.getPublishEvents();
|
||||
|
||||
for(var i = 0; i < this.db.modules.length; i++) {
|
||||
var mod = this.db.modules[i];
|
||||
var events = mod.getPublishEvents();
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
var params = Array.prototype.slice.call(arguments).slice(1);
|
||||
params.unshift(this);
|
||||
if (name === event.name) {
|
||||
event.subscribe.apply(event.scope, params);
|
||||
}
|
||||
events = events.concat(mod.getPublishEvents());
|
||||
}
|
||||
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
var params = Array.prototype.slice.call(arguments).slice(1);
|
||||
params.unshift(this);
|
||||
if (name === event.name) {
|
||||
event.subscribe.apply(event.scope, params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bus.prototype.unsubscribe = function(name) {
|
||||
for (var i = 0; i < this.db.modules.length; i++) {
|
||||
var events = this.db.getPublishEvents();
|
||||
|
||||
for(var i = 0; i < this.db.modules.length; i++) {
|
||||
var mod = this.db.modules[i];
|
||||
var events = mod.getPublishEvents();
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
var params = Array.prototype.slice.call(arguments).slice(1);
|
||||
params.unshift(this);
|
||||
if (name === event.name) {
|
||||
event.unsubscribe.apply(event.scope, params);
|
||||
}
|
||||
events = events.concat(mod.getPublishEvents());
|
||||
}
|
||||
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
var params = Array.prototype.slice.call(arguments).slice(1);
|
||||
params.unshift(this);
|
||||
if (name === event.name) {
|
||||
event.unsubscribe.apply(event.scope, params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bus.prototype.close = function() {
|
||||
// Unsubscribe from all events
|
||||
for (var i = 0; i < this.db.modules.length; i++) {
|
||||
var events = this.db.getPublishEvents();
|
||||
|
||||
for(var i = 0; i < this.db.modules.length; i++) {
|
||||
var mod = this.db.modules[i];
|
||||
var events = mod.getPublishEvents();
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
event.unsubscribe.call(event.scope, this);
|
||||
}
|
||||
events = events.concat(mod.getPublishEvents());
|
||||
}
|
||||
|
||||
// Unsubscribe from all events
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
event.unsubscribe.call(event.scope, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
90
lib/db.js
90
lib/db.js
|
@ -32,10 +32,25 @@ function DB(options) {
|
|||
|
||||
this.modules = [];
|
||||
|
||||
this.subscriptions = {
|
||||
transaction: [],
|
||||
block: []
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(DB, BaseDB);
|
||||
|
||||
DB.prototype.initialize = function() {
|
||||
// Add all db option modules
|
||||
if(this._modules && this._modules.length) {
|
||||
for(var i = 0; i < this._modules.length; i++) {
|
||||
this.addModule(this._modules[i]);
|
||||
}
|
||||
}
|
||||
this.bitcoind.on('tx', this.transactionHandler.bind(this));
|
||||
this.emit('ready');
|
||||
}
|
||||
|
||||
DB.prototype.getBlock = function(hash, callback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -91,6 +106,34 @@ DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback
|
|||
});
|
||||
};
|
||||
|
||||
DB.prototype.sendTransaction = function(tx, callback) {
|
||||
if(tx instanceof this.Transaction) {
|
||||
tx = tx.toString();
|
||||
}
|
||||
$.checkArgument(typeof tx === 'string', 'Argument must be a hex string or Transaction');
|
||||
|
||||
try {
|
||||
var txid = this.bitcoind.sendTransaction(tx);
|
||||
return callback(null, txid);
|
||||
} catch(err) {
|
||||
return callback(err);
|
||||
}
|
||||
};
|
||||
|
||||
DB.prototype.estimateFee = function(blocks, callback) {
|
||||
var self = this;
|
||||
|
||||
// For some reason getting fee for 1 block returns -1
|
||||
// Until this is resolved, just make it 2 blocks
|
||||
if(blocks === 1) {
|
||||
blocks = 2;
|
||||
}
|
||||
|
||||
setImmediate(function() {
|
||||
callback(null, self.bitcoind.estimateFee(blocks));
|
||||
});
|
||||
}
|
||||
|
||||
DB.prototype.validateBlockData = function(block, callback) {
|
||||
// bitcoind does the validation
|
||||
setImmediate(callback);
|
||||
|
@ -195,6 +238,11 @@ DB.prototype.blockHandler = function(block, add, callback) {
|
|||
var self = this;
|
||||
var operations = [];
|
||||
|
||||
// Notify block subscribers
|
||||
for(var i = 0; i < this.subscriptions.block.length; i++) {
|
||||
this.subscriptions.transaction[i].emit('block', block.hash);
|
||||
}
|
||||
|
||||
async.eachSeries(
|
||||
this.modules,
|
||||
function(module, next) {
|
||||
|
@ -222,7 +270,9 @@ DB.prototype.blockHandler = function(block, add, callback) {
|
|||
DB.prototype.getAPIMethods = function() {
|
||||
var methods = [
|
||||
['getBlock', this, this.getBlock, 1],
|
||||
['getTransaction', this, this.getTransaction, 2]
|
||||
['getTransaction', this, this.getTransaction, 2],
|
||||
['sendTransaction', this, this.sendTransaction, 1],
|
||||
['estimateFee', this, this.estimateFee, 1]
|
||||
];
|
||||
|
||||
for(var i = 0; i < this.modules.length; i++) {
|
||||
|
@ -232,6 +282,23 @@ DB.prototype.getAPIMethods = function() {
|
|||
return methods;
|
||||
};
|
||||
|
||||
DB.prototype.getPublishEvents = function() {
|
||||
return [
|
||||
{
|
||||
name: 'transaction',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'transaction'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'transaction')
|
||||
},
|
||||
{
|
||||
name: 'block',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'block'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'block')
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
DB.prototype.addModule = function(Module) {
|
||||
var module = new Module({
|
||||
db: this
|
||||
|
@ -240,4 +307,25 @@ DB.prototype.addModule = function(Module) {
|
|||
this.modules.push(module);
|
||||
};
|
||||
|
||||
DB.prototype.subscribe = function(name, emitter) {
|
||||
this.subscriptions[name].push(emitter);
|
||||
};
|
||||
|
||||
DB.prototype.unsubscribe = function(name, emitter) {
|
||||
var index = this.subscriptions[name].indexOf(emitter);
|
||||
if(index > -1) {
|
||||
this.subscriptions[name].splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
DB.prototype.transactionHandler = function(txInfo) {
|
||||
var tx = bitcore.Transaction().fromBuffer(txInfo.buffer);
|
||||
for(var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||
this.subscriptions.transaction[i].emit('transaction', {
|
||||
rejected: !txInfo.mempool,
|
||||
tx: tx
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DB;
|
||||
|
|
|
@ -18,8 +18,8 @@ var AddressModule = function(options) {
|
|||
BaseModule.call(this, options);
|
||||
|
||||
this.subscriptions = {};
|
||||
this.subscriptions.transaction = {};
|
||||
this.subscriptions.balance = {};
|
||||
this.subscriptions['address/transaction'] = {};
|
||||
this.subscriptions['address/balance'] = {};
|
||||
|
||||
this.db.bitcoind.on('tx', this.transactionHandler.bind(this));
|
||||
|
||||
|
@ -45,17 +45,17 @@ AddressModule.prototype.getAPIMethods = function() {
|
|||
AddressModule.prototype.getPublishEvents = function() {
|
||||
return [
|
||||
{
|
||||
name: 'transaction',
|
||||
name: 'address/transaction',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'transaction'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'transaction')
|
||||
subscribe: this.subscribe.bind(this, 'address/transaction'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'address/transaction')
|
||||
},
|
||||
{
|
||||
name: 'balance',
|
||||
name: 'address/balance',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'balance'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'balance')
|
||||
}
|
||||
subscribe: this.subscribe.bind(this, 'address/balance'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'address/balance')
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
|
@ -123,7 +123,6 @@ AddressModule.prototype.transactionHandler = function(txInfo) {
|
|||
for (var key in messages) {
|
||||
this.transactionEventHandler(messages[key]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
AddressModule.prototype.blockHandler = function(block, addOutput, callback) {
|
||||
|
@ -234,24 +233,24 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) {
|
|||
* @param {Boolean} [obj.rejected] - If the transaction was not accepted in the mempool
|
||||
*/
|
||||
AddressModule.prototype.transactionEventHandler = function(obj) {
|
||||
if(this.subscriptions.transaction[obj.address]) {
|
||||
var emitters = this.subscriptions.transaction[obj.address];
|
||||
for(var k = 0; k < emitters.length; k++) {
|
||||
emitters[k].emit('transaction', obj);
|
||||
if(this.subscriptions['address/transaction'][obj.address]) {
|
||||
var emitters = this.subscriptions['address/transaction'][obj.address];
|
||||
for(var i = 0; i < emitters.length; i++) {
|
||||
emitters[i].emit('address/transaction', obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddressModule.prototype.balanceEventHandler = function(block, address) {
|
||||
if(this.subscriptions.balance[address]) {
|
||||
var emitters = this.subscriptions.balance[address];
|
||||
if(this.subscriptions['address/balance'][address]) {
|
||||
var emitters = this.subscriptions['address/balance'][address];
|
||||
this.getBalance(address, true, function(err, balance) {
|
||||
if(err) {
|
||||
return this.emit(err);
|
||||
}
|
||||
|
||||
for(var i = 0; i < emitters.length; i++) {
|
||||
emitters[i].emit('balance', address, balance, block);
|
||||
emitters[i].emit('address/balance', address, balance, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -338,9 +337,11 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback
|
|||
address: addressStr,
|
||||
txid: key[3],
|
||||
outputIndex: Number(key[4]),
|
||||
timestamp: key[2],
|
||||
satoshis: Number(value[0]),
|
||||
script: value[1],
|
||||
blockHeight: Number(value[2])
|
||||
blockHeight: Number(value[2]),
|
||||
confirmations: self.db.chain.tip.__height - Number(value[2]) + 1
|
||||
};
|
||||
|
||||
outputs.push(output);
|
||||
|
@ -371,7 +372,32 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback
|
|||
|
||||
};
|
||||
|
||||
AddressModule.prototype.getUnspentOutputs = function(address, queryMempool, callback) {
|
||||
AddressModule.prototype.getUnspentOutputs = function(addresses, queryMempool, callback) {
|
||||
var self = this;
|
||||
|
||||
if(!Array.isArray(addresses)) {
|
||||
addresses = [addresses];
|
||||
}
|
||||
|
||||
var utxos = [];
|
||||
|
||||
async.eachSeries(addresses, function(address, next) {
|
||||
self.getUnspentOutputsForAddress(address, queryMempool, function(err, unspents) {
|
||||
if(err && err instanceof errors.NoOutputs) {
|
||||
return next();
|
||||
} else if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
utxos = utxos.concat(unspents);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
callback(err, utxos);
|
||||
});
|
||||
};
|
||||
|
||||
AddressModule.prototype.getUnspentOutputsForAddress = function(address, queryMempool, callback) {
|
||||
|
||||
var self = this;
|
||||
|
||||
|
@ -427,7 +453,30 @@ AddressModule.prototype.getSpendInfoForOutput = function(txid, outputIndex, call
|
|||
});
|
||||
};
|
||||
|
||||
AddressModule.prototype.getAddressHistory = function(address, queryMempool, callback) {
|
||||
AddressModule.prototype.getAddressHistory = function(addresses, queryMempool, callback) {
|
||||
var self = this;
|
||||
|
||||
if(!Array.isArray(addresses)) {
|
||||
addresses = [addresses];
|
||||
}
|
||||
|
||||
var history = [];
|
||||
|
||||
async.eachSeries(addresses, function(address, next) {
|
||||
self.getAddressHistoryForAddress(address, queryMempool, function(err, h) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
history = history.concat(h);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
callback(err, history);
|
||||
});
|
||||
};
|
||||
|
||||
AddressModule.prototype.getAddressHistoryForAddress = function(address, queryMempool, callback) {
|
||||
var self = this;
|
||||
|
||||
var txinfos = {};
|
||||
|
@ -448,12 +497,14 @@ AddressModule.prototype.getAddressHistory = function(address, queryMempool, call
|
|||
}
|
||||
|
||||
txinfos[transaction.hash] = {
|
||||
address: address,
|
||||
satoshis: 0,
|
||||
height: transaction.__height,
|
||||
confirmations: self.db.chain.tip.__height - transaction.__height + 1,
|
||||
timestamp: transaction.__timestamp,
|
||||
outputIndexes: [],
|
||||
inputIndexes: [],
|
||||
transaction: transaction
|
||||
tx: transaction
|
||||
};
|
||||
|
||||
callback(null, txinfos[transaction.hash]);
|
||||
|
@ -490,7 +541,7 @@ AddressModule.prototype.getAddressHistory = function(address, queryMempool, call
|
|||
}
|
||||
|
||||
txinfo.inputIndexes.push(spendInfo.inputIndex);
|
||||
txinfo.satoshis -= txinfo.transaction.inputs[spendInfo.inputIndex].output.satoshis;
|
||||
txinfo.satoshis -= txinfo.tx.inputs[spendInfo.inputIndex].output.satoshis;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
|
11
lib/node.js
11
lib/node.js
|
@ -39,7 +39,7 @@ Node.prototype.getAllAPIMethods = function() {
|
|||
};
|
||||
|
||||
Node.prototype.getAllPublishEvents = function() {
|
||||
var events = [];
|
||||
var events = this.db.getPublishEvents();
|
||||
for (var i = 0; i < this.db.modules.length; i++) {
|
||||
var mod = this.db.modules[i];
|
||||
events = events.concat(mod.getPublishEvents());
|
||||
|
@ -407,15 +407,6 @@ Node.prototype._initializeDatabase = function() {
|
|||
|
||||
// Database
|
||||
this.db.on('ready', function() {
|
||||
|
||||
// Add all db option modules
|
||||
var modules = self.db._modules;
|
||||
if(modules && modules.length) {
|
||||
for(var i = 0; i < modules.length; i++) {
|
||||
self.db.addModule(modules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
log.info('Bitcoin Database Ready');
|
||||
self.chain.initialize();
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ var chainlib = require('chainlib');
|
|||
var BaseTransaction = chainlib.Transaction;
|
||||
var BaseDatabase = chainlib.DB;
|
||||
var levelup = chainlib.deps.levelup;
|
||||
var _ = bitcore.deps._;
|
||||
|
||||
Transaction.prototype.populateInputs = function(db, poolTransactions, callback) {
|
||||
var self = this;
|
||||
|
@ -29,7 +30,7 @@ Transaction.prototype._populateInput = function(db, input, poolTransactions, cal
|
|||
return callback(new Error('Input is expected to have prevTxId as a buffer'));
|
||||
}
|
||||
var txid = input.prevTxId.toString('hex');
|
||||
db.getTransaction(txid, false, function(err, prevTx) {
|
||||
db.getTransaction(txid, true, function(err, prevTx) {
|
||||
if(err instanceof levelup.errors.NotFoundError) {
|
||||
// Check the pool for transaction
|
||||
for(var i = 0; i < poolTransactions.length; i++) {
|
||||
|
@ -64,4 +65,39 @@ Transaction.manyToBuffer = function(transactions) {
|
|||
return BaseTransaction.manyToBuffer(transactions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Override Bitcore's toObject() to include populated inputs and txid
|
||||
*/
|
||||
Transaction.prototype.toObject = function toObject() {
|
||||
var inputs = [];
|
||||
this.inputs.forEach(function(input) {
|
||||
var inputObj = input.toObject();
|
||||
if(input.output) {
|
||||
inputObj.output = input.output.toObject();
|
||||
}
|
||||
inputs.push(inputObj);
|
||||
});
|
||||
var outputs = [];
|
||||
this.outputs.forEach(function(output) {
|
||||
outputs.push(output.toObject());
|
||||
});
|
||||
var obj = {
|
||||
txid: this.id,
|
||||
version: this.version,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
nLockTime: this.nLockTime
|
||||
};
|
||||
if (this._changeScript) {
|
||||
obj.changeScript = this._changeScript.toString();
|
||||
}
|
||||
if (!_.isUndefined(this._changeIndex)) {
|
||||
obj.changeIndex = this._changeIndex;
|
||||
}
|
||||
if (!_.isUndefined(this._fee)) {
|
||||
obj.fee = this._fee;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
module.exports = Transaction;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
var chainlib = require('chainlib');
|
||||
var utils = chainlib.utils;
|
||||
|
||||
/**
|
||||
* Bitcore's API does not implement toJSON in the standard way.
|
||||
* This causes issues when doing a JSON.stringify on an object
|
||||
* which contains Bitcore objects. This custom implmentation
|
||||
* of stringify accounts for Bitcore objects.
|
||||
* @param {Object} obj
|
||||
* @return {String} json
|
||||
*/
|
||||
utils.stringify = function(obj) {
|
||||
return JSON.stringify(utils.expandObject(obj));
|
||||
}
|
||||
|
||||
utils.expandObject = function(obj) {
|
||||
if(Array.isArray(obj)) {
|
||||
var expandedArray = [];
|
||||
for(var i = 0; i < obj.length; i++) {
|
||||
expandedArray.push(utils.expandObject(obj[i]));
|
||||
}
|
||||
|
||||
return expandedArray;
|
||||
} else if(typeof obj === 'function' || typeof obj === 'object') {
|
||||
if(obj.toObject) {
|
||||
return obj.toObject();
|
||||
} else if(obj.toJSON) {
|
||||
return obj.toJSON();
|
||||
} else {
|
||||
var expandedObj = {};
|
||||
|
||||
for(var key in obj) {
|
||||
expandedObj[key] = utils.expandObject(obj[key]);
|
||||
}
|
||||
|
||||
return expandedObj;
|
||||
}
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = utils;
|
|
@ -1566,7 +1566,10 @@ NAN_METHOD(GetMempoolOutputs) {
|
|||
|
||||
Local<Object> output = NanNew<Object>();
|
||||
|
||||
output->Set(NanNew<String>("script"), NanNew<String>(script.ToString()));
|
||||
output->Set(NanNew<String>("address"), NanNew<String>(psz));
|
||||
|
||||
std::string scriptHex = HexStr(script.begin(), script.end());
|
||||
output->Set(NanNew<String>("script"), NanNew<String>(scriptHex));
|
||||
|
||||
uint64_t satoshis = txout.nValue;
|
||||
output->Set(NanNew<String>("satoshis"), NanNew<Number>(satoshis)); // can't go above 2 ^ 53 -1
|
||||
|
|
|
@ -7,68 +7,107 @@ var Bus = require('../lib/bus');
|
|||
describe('Bus', function() {
|
||||
|
||||
describe('#subscribe', function() {
|
||||
it('will call modules subscribe function with the correct arguments', function() {
|
||||
var subscribe = sinon.spy();
|
||||
it('will call db and modules subscribe function with the correct arguments', function() {
|
||||
var subscribeDb = sinon.spy();
|
||||
var subscribeModule = sinon.spy();
|
||||
var db = {
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'dbtest',
|
||||
scope: this,
|
||||
subscribe: subscribeDb
|
||||
}
|
||||
]
|
||||
),
|
||||
modules: [
|
||||
{
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'test',
|
||||
scope: this,
|
||||
subscribe: subscribe,
|
||||
subscribe: subscribeModule,
|
||||
}
|
||||
])
|
||||
}
|
||||
]
|
||||
};
|
||||
var bus = new Bus({db: db});
|
||||
bus.subscribe('dbtest', 'a', 'b', 'c');
|
||||
bus.subscribe('test', 'a', 'b', 'c');
|
||||
subscribe.callCount.should.equal(1);
|
||||
subscribe.args[0][0].should.equal(bus);
|
||||
subscribe.args[0][1].should.equal('a');
|
||||
subscribe.args[0][2].should.equal('b');
|
||||
subscribe.args[0][3].should.equal('c');
|
||||
subscribeModule.callCount.should.equal(1);
|
||||
subscribeDb.callCount.should.equal(1);
|
||||
subscribeDb.args[0][0].should.equal(bus);
|
||||
subscribeDb.args[0][1].should.equal('a');
|
||||
subscribeDb.args[0][2].should.equal('b');
|
||||
subscribeDb.args[0][3].should.equal('c');
|
||||
subscribeModule.args[0][0].should.equal(bus);
|
||||
subscribeModule.args[0][1].should.equal('a');
|
||||
subscribeModule.args[0][2].should.equal('b');
|
||||
subscribeModule.args[0][3].should.equal('c');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#unsubscribe', function() {
|
||||
it('will call modules unsubscribe function with the correct arguments', function() {
|
||||
var unsubscribe = sinon.spy();
|
||||
it('will call db and modules unsubscribe function with the correct arguments', function() {
|
||||
var unsubscribeDb = sinon.spy();
|
||||
var unsubscribeModule = sinon.spy();
|
||||
var db = {
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'dbtest',
|
||||
scope: this,
|
||||
unsubscribe: unsubscribeDb
|
||||
}
|
||||
]
|
||||
),
|
||||
modules: [
|
||||
{
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'test',
|
||||
scope: this,
|
||||
unsubscribe: unsubscribe
|
||||
unsubscribe: unsubscribeModule,
|
||||
}
|
||||
])
|
||||
}
|
||||
]
|
||||
};
|
||||
var bus = new Bus({db: db});
|
||||
bus.unsubscribe('dbtest', 'a', 'b', 'c');
|
||||
bus.unsubscribe('test', 'a', 'b', 'c');
|
||||
unsubscribe.callCount.should.equal(1);
|
||||
unsubscribe.args[0][0].should.equal(bus);
|
||||
unsubscribe.args[0][1].should.equal('a');
|
||||
unsubscribe.args[0][2].should.equal('b');
|
||||
unsubscribe.args[0][3].should.equal('c');
|
||||
unsubscribeModule.callCount.should.equal(1);
|
||||
unsubscribeDb.callCount.should.equal(1);
|
||||
unsubscribeDb.args[0][0].should.equal(bus);
|
||||
unsubscribeDb.args[0][1].should.equal('a');
|
||||
unsubscribeDb.args[0][2].should.equal('b');
|
||||
unsubscribeDb.args[0][3].should.equal('c');
|
||||
unsubscribeModule.args[0][0].should.equal(bus);
|
||||
unsubscribeModule.args[0][1].should.equal('a');
|
||||
unsubscribeModule.args[0][2].should.equal('b');
|
||||
unsubscribeModule.args[0][3].should.equal('c');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#close', function() {
|
||||
it('will unsubscribe from all events', function() {
|
||||
var unsubscribe = sinon.spy();
|
||||
var unsubscribeDb = sinon.spy();
|
||||
var unsubscribeModule = sinon.spy();
|
||||
var db = {
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'dbtest',
|
||||
scope: this,
|
||||
unsubscribe: unsubscribeDb
|
||||
}
|
||||
]
|
||||
),
|
||||
modules: [
|
||||
{
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'test',
|
||||
scope: this,
|
||||
unsubscribe: unsubscribe
|
||||
unsubscribe: unsubscribeModule
|
||||
}
|
||||
])
|
||||
}
|
||||
|
@ -78,9 +117,12 @@ describe('Bus', function() {
|
|||
var bus = new Bus({db: db});
|
||||
bus.close();
|
||||
|
||||
unsubscribe.callCount.should.equal(1);
|
||||
unsubscribe.args[0].length.should.equal(1);
|
||||
unsubscribe.args[0][0].should.equal(bus);
|
||||
unsubscribeDb.callCount.should.equal(1);
|
||||
unsubscribeModule.callCount.should.equal(1);
|
||||
unsubscribeDb.args[0].length.should.equal(1);
|
||||
unsubscribeDb.args[0][0].should.equal(bus);
|
||||
unsubscribeModule.args[0].length.should.equal(1);
|
||||
unsubscribeModule.args[0][0].should.equal(bus);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -301,7 +301,7 @@ describe('Bitcoin DB', function() {
|
|||
var db = new DB({store: memdown});
|
||||
db.modules = [];
|
||||
var methods = db.getAPIMethods();
|
||||
methods.length.should.equal(2);
|
||||
methods.length.should.equal(4);
|
||||
});
|
||||
|
||||
it('should also return modules API methods', function() {
|
||||
|
@ -325,7 +325,7 @@ describe('Bitcoin DB', function() {
|
|||
db.modules = [module1, module2];
|
||||
|
||||
var methods = db.getAPIMethods();
|
||||
methods.length.should.equal(5);
|
||||
methods.length.should.equal(7);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ describe('AddressModule', function() {
|
|||
it('will emit a transaction if there is a subscriber', function(done) {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
var emitter = new EventEmitter();
|
||||
am.subscriptions.transaction = {
|
||||
am.subscriptions['address/transaction'] = {
|
||||
'1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter]
|
||||
};
|
||||
var block = {
|
||||
|
@ -265,7 +265,7 @@ describe('AddressModule', function() {
|
|||
timestamp: new Date()
|
||||
};
|
||||
var tx = {};
|
||||
emitter.on('transaction', function(obj) {
|
||||
emitter.on('address/transaction', function(obj) {
|
||||
obj.address.should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N');
|
||||
obj.tx.should.equal(tx);
|
||||
obj.timestamp.should.equal(block.timestamp);
|
||||
|
@ -287,13 +287,13 @@ describe('AddressModule', function() {
|
|||
it('will emit a balance if there is a subscriber', function(done) {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
var emitter = new EventEmitter();
|
||||
am.subscriptions.balance = {
|
||||
am.subscriptions['address/balance'] = {
|
||||
'1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter]
|
||||
};
|
||||
var block = {};
|
||||
var balance = 1000;
|
||||
am.getBalance = sinon.stub().callsArgWith(2, null, balance);
|
||||
emitter.on('balance', function(address, bal, b) {
|
||||
emitter.on('address/balance', function(address, bal, b) {
|
||||
address.should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N');
|
||||
bal.should.equal(balance);
|
||||
b.should.equal(block);
|
||||
|
@ -309,33 +309,33 @@ describe('AddressModule', function() {
|
|||
var emitter = new EventEmitter();
|
||||
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
var name = 'transaction';
|
||||
var name = 'address/transaction';
|
||||
am.subscribe(name, emitter, [address]);
|
||||
am.subscriptions.transaction[address].should.deep.equal([emitter]);
|
||||
am.subscriptions['address/transaction'][address].should.deep.equal([emitter]);
|
||||
|
||||
var address2 = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||
am.subscribe(name, emitter, [address2]);
|
||||
am.subscriptions.transaction[address2].should.deep.equal([emitter]);
|
||||
am.subscriptions['address/transaction'][address2].should.deep.equal([emitter]);
|
||||
|
||||
var emitter2 = new EventEmitter();
|
||||
am.subscribe(name, emitter2, [address]);
|
||||
am.subscriptions.transaction[address].should.deep.equal([emitter, emitter2]);
|
||||
am.subscriptions['address/transaction'][address].should.deep.equal([emitter, emitter2]);
|
||||
});
|
||||
it('will add an emitter to the subscribers array (balance)', function() {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
var emitter = new EventEmitter();
|
||||
var name = 'balance';
|
||||
var name = 'address/balance';
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
am.subscribe(name, emitter, [address]);
|
||||
am.subscriptions.balance[address].should.deep.equal([emitter]);
|
||||
am.subscriptions['address/balance'][address].should.deep.equal([emitter]);
|
||||
|
||||
var address2 = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||
am.subscribe(name, emitter, [address2]);
|
||||
am.subscriptions.balance[address2].should.deep.equal([emitter]);
|
||||
am.subscriptions['address/balance'][address2].should.deep.equal([emitter]);
|
||||
|
||||
var emitter2 = new EventEmitter();
|
||||
am.subscribe(name, emitter2, [address]);
|
||||
am.subscriptions.balance[address].should.deep.equal([emitter, emitter2]);
|
||||
am.subscriptions['address/balance'][address].should.deep.equal([emitter, emitter2]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -345,31 +345,31 @@ describe('AddressModule', function() {
|
|||
var emitter = new EventEmitter();
|
||||
var emitter2 = new EventEmitter();
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
am.subscriptions.transaction[address] = [emitter, emitter2];
|
||||
var name = 'transaction';
|
||||
am.subscriptions['address/transaction'][address] = [emitter, emitter2];
|
||||
var name = 'address/transaction';
|
||||
am.unsubscribe(name, emitter, [address]);
|
||||
am.subscriptions.transaction[address].should.deep.equal([emitter2]);
|
||||
am.subscriptions['address/transaction'][address].should.deep.equal([emitter2]);
|
||||
});
|
||||
it('will remove emitter from subscribers array (balance)', function() {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
var emitter = new EventEmitter();
|
||||
var emitter2 = new EventEmitter();
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
var name = 'balance';
|
||||
am.subscriptions.balance[address] = [emitter, emitter2];
|
||||
var name = 'address/balance';
|
||||
am.subscriptions['address/balance'][address] = [emitter, emitter2];
|
||||
am.unsubscribe(name, emitter, [address]);
|
||||
am.subscriptions.balance[address].should.deep.equal([emitter2]);
|
||||
am.subscriptions['address/balance'][address].should.deep.equal([emitter2]);
|
||||
});
|
||||
it('should unsubscribe from all addresses if no addresses are specified', function() {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
var emitter = new EventEmitter();
|
||||
var emitter2 = new EventEmitter();
|
||||
am.subscriptions.balance = {
|
||||
am.subscriptions['address/balance'] = {
|
||||
'1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W': [emitter, emitter2],
|
||||
'1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter2, emitter]
|
||||
};
|
||||
am.unsubscribe('balance', emitter);
|
||||
am.subscriptions.balance.should.deep.equal({
|
||||
am.unsubscribe('address/balance', emitter);
|
||||
am.subscriptions['address/balance'].should.deep.equal({
|
||||
'1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W': [emitter2],
|
||||
'1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter2]
|
||||
});
|
||||
|
@ -408,6 +408,11 @@ describe('AddressModule', function() {
|
|||
var db = {
|
||||
bitcoind: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
chain: {
|
||||
tip: {
|
||||
__height: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -489,7 +494,7 @@ describe('AddressModule', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#getUnspentOutputs', function() {
|
||||
describe('#getUnspentOutputsForAddress', function() {
|
||||
it('should filter out spent outputs', function(done) {
|
||||
var outputs = [
|
||||
{
|
||||
|
@ -514,7 +519,7 @@ describe('AddressModule', function() {
|
|||
i++;
|
||||
};
|
||||
|
||||
am.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
am.getUnspentOutputsForAddress('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.not.exist(err);
|
||||
outputs.length.should.equal(2);
|
||||
outputs[0].satoshis.should.equal(1000);
|
||||
|
@ -525,7 +530,7 @@ describe('AddressModule', function() {
|
|||
it('should handle an error from getOutputs', function(done) {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
am.getOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
am.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
am.getUnspentOutputsForAddress('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
done();
|
||||
|
@ -534,7 +539,7 @@ describe('AddressModule', function() {
|
|||
it('should handle when there are no outputs', function(done) {
|
||||
var am = new AddressModule({db: mockdb});
|
||||
am.getOutputs = sinon.stub().callsArgWith(2, null, []);
|
||||
am.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
am.getUnspentOutputsForAddress('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.should.be.instanceof(errors.NoOutputs);
|
||||
outputs.length.should.equal(0);
|
||||
|
@ -709,6 +714,11 @@ describe('AddressModule', function() {
|
|||
},
|
||||
bitcoind: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
chain: {
|
||||
tip: {
|
||||
__height: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
var am = new AddressModule({db: db});
|
||||
|
@ -733,23 +743,23 @@ describe('AddressModule', function() {
|
|||
it('should give transaction history for an address', function(done) {
|
||||
am.getAddressHistory('address', true, function(err, history) {
|
||||
should.not.exist(err);
|
||||
history[0].transaction.hash.should.equal('tx1');
|
||||
history[0].tx.hash.should.equal('tx1');
|
||||
history[0].satoshis.should.equal(5000);
|
||||
history[0].height.should.equal(1);
|
||||
history[0].timestamp.should.equal(1438289011844);
|
||||
history[1].transaction.hash.should.equal('tx2');
|
||||
history[1].tx.hash.should.equal('tx2');
|
||||
history[1].satoshis.should.equal(-5000);
|
||||
history[1].height.should.equal(2);
|
||||
history[1].timestamp.should.equal(1438289021844);
|
||||
history[2].transaction.hash.should.equal('tx3');
|
||||
history[2].tx.hash.should.equal('tx3');
|
||||
history[2].satoshis.should.equal(2000);
|
||||
history[2].height.should.equal(3);
|
||||
history[2].timestamp.should.equal(1438289031844);
|
||||
history[3].transaction.hash.should.equal('tx4');
|
||||
history[3].tx.hash.should.equal('tx4');
|
||||
history[3].satoshis.should.equal(3000);
|
||||
history[3].height.should.equal(4);
|
||||
history[3].timestamp.should.equal(1438289041844);
|
||||
history[4].transaction.hash.should.equal('tx5');
|
||||
history[4].tx.hash.should.equal('tx5');
|
||||
history[4].satoshis.should.equal(-3000);
|
||||
history[4].height.should.equal(5);
|
||||
history[4].timestamp.should.equal(1438289051844);
|
||||
|
|
|
@ -71,6 +71,7 @@ describe('Bitcoind Node', function() {
|
|||
it('should return modules publish events', function() {
|
||||
var node = new Node({});
|
||||
var db = {
|
||||
getPublishEvents: sinon.stub().returns(['db1', 'db2']),
|
||||
modules: [
|
||||
{
|
||||
getPublishEvents: sinon.stub().returns(['mda1', 'mda2'])
|
||||
|
@ -83,7 +84,7 @@ describe('Bitcoind Node', function() {
|
|||
node.db = db;
|
||||
|
||||
var events = node.getAllPublishEvents();
|
||||
events.should.deep.equal(['mda1', 'mda2', 'mdb1', 'mdb2']);
|
||||
events.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
||||
});
|
||||
});
|
||||
describe('#_loadConfiguration', function() {
|
||||
|
@ -462,7 +463,6 @@ describe('Bitcoind Node', function() {
|
|||
setImmediate(function() {
|
||||
chainlib.log.info.callCount.should.equal(1);
|
||||
chainlib.log.info.restore();
|
||||
node.db.addModule.callCount.should.equal(1);
|
||||
node.chain.initialize.callCount.should.equal(1);
|
||||
done();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue