2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* bitcoind.js
|
|
|
|
* Copyright (c) 2014, BitPay (MIT License)
|
|
|
|
* A bitcoind node.js binding.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var net = require('net');
|
|
|
|
var EventEmitter = require('events').EventEmitter;
|
|
|
|
var bitcoindjs = require('../build/Release/bitcoindjs.node');
|
2014-09-02 19:28:20 -07:00
|
|
|
var util = require('util');
|
2014-09-17 15:33:21 -07:00
|
|
|
var net = require('net');
|
2014-08-12 12:03:04 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Bitcoin
|
|
|
|
*/
|
|
|
|
|
2014-08-29 13:54:54 -07:00
|
|
|
function Bitcoin(options) {
|
2014-08-12 12:03:04 -07:00
|
|
|
var self = this;
|
|
|
|
|
2014-08-19 16:40:19 -07:00
|
|
|
if (!(this instanceof Bitcoin)) {
|
2014-08-29 13:54:54 -07:00
|
|
|
return new Bitcoin(options);
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EventEmitter.call(this);
|
|
|
|
|
2014-08-29 13:54:54 -07:00
|
|
|
this.options = options;
|
2014-09-11 17:18:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Bitcoin.prototype.__proto__ = EventEmitter.prototype;
|
|
|
|
|
|
|
|
Bitcoin.prototype.start = function(callback) {
|
|
|
|
var self = this;
|
2014-08-29 13:54:54 -07:00
|
|
|
|
2014-09-17 14:21:28 -07:00
|
|
|
var none = {};
|
2014-09-22 14:35:11 -07:00
|
|
|
var isSignal = {};
|
|
|
|
var sigint = { name: 'SIGINT', signal: isSignal };
|
|
|
|
var sighup = { name: 'SIGHUP', signal: isSignal };
|
|
|
|
var sigquit = { name: 'SIGQUIT', signal: isSignal };
|
2014-09-17 14:21:28 -07:00
|
|
|
var exitCaught = none;
|
|
|
|
var errorCaught = none;
|
|
|
|
|
2014-09-02 19:28:20 -07:00
|
|
|
this.log_pipe = bitcoindjs.start(function(err, status) {
|
2014-09-22 14:35:11 -07:00
|
|
|
[sigint, sighup, sigquit].forEach(function(signal) {
|
|
|
|
process.on(signal.name, signal.listener = function() {
|
|
|
|
if (process.listeners(signal.name).length > 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!self._shutdown) {
|
|
|
|
process.exit(0);
|
|
|
|
} else {
|
|
|
|
self.stop();
|
|
|
|
exitCaught = signal;
|
|
|
|
}
|
|
|
|
});
|
2014-09-19 16:45:46 -07:00
|
|
|
});
|
2014-09-17 14:21:28 -07:00
|
|
|
|
|
|
|
var exit = process.exit;
|
|
|
|
self._exit = function() {
|
|
|
|
return exit.apply(process, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
process.exit = function(code) {
|
|
|
|
exitCaught = code || 0;
|
|
|
|
if (!self._shutdown) {
|
2014-09-18 14:54:08 -07:00
|
|
|
return self._exit(code);
|
2014-09-17 14:21:28 -07:00
|
|
|
}
|
|
|
|
self.stop();
|
|
|
|
};
|
2014-09-17 14:08:26 -07:00
|
|
|
|
2014-09-17 14:21:28 -07:00
|
|
|
process.on('uncaughtException', function(err) {
|
2014-09-18 14:54:08 -07:00
|
|
|
if (process.listeners('uncaughtException').length > 1) {
|
|
|
|
return;
|
|
|
|
}
|
2014-09-17 14:21:28 -07:00
|
|
|
errorCaught = err;
|
|
|
|
if (!self._shutdown) {
|
2014-09-18 14:54:08 -07:00
|
|
|
if (err && err.stack) {
|
|
|
|
console.error(err.stack);
|
|
|
|
}
|
|
|
|
self._exit(1);
|
|
|
|
return;
|
2014-09-17 14:21:28 -07:00
|
|
|
}
|
|
|
|
self.stop();
|
|
|
|
});
|
2014-09-17 14:25:19 -07:00
|
|
|
|
|
|
|
if (callback) {
|
|
|
|
callback(err);
|
|
|
|
callback = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
self.emit('error', err);
|
|
|
|
} else {
|
|
|
|
self.emit('open', status);
|
|
|
|
}
|
2014-08-12 12:03:04 -07:00
|
|
|
});
|
2014-09-02 19:28:20 -07:00
|
|
|
|
2014-09-10 16:57:18 -07:00
|
|
|
// bitcoind's boost threads aren't in the thread pool
|
|
|
|
// or on node's event loop, so we need to keep node open.
|
2014-09-17 14:08:26 -07:00
|
|
|
this._shutdown = setInterval(function() {
|
|
|
|
if (!self._stoppingSaid && bitcoindjs.stopping()) {
|
|
|
|
self._stoppingSaid = true;
|
|
|
|
self.log('shutting down...');
|
|
|
|
}
|
2014-09-17 14:21:28 -07:00
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
if (bitcoindjs.stopped()) {
|
2014-09-17 14:08:26 -07:00
|
|
|
self.log('shut down.');
|
2014-09-17 14:21:28 -07:00
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
clearInterval(self._shutdown);
|
|
|
|
delete self._shutdown;
|
2014-09-17 14:21:28 -07:00
|
|
|
|
|
|
|
if (exitCaught !== none) {
|
2014-09-22 14:35:11 -07:00
|
|
|
if (exitCaught.signal === isSignal) {
|
|
|
|
process.removeListener(exitCaught.name, exitCaught.listener);
|
|
|
|
setImmediate(function() {
|
|
|
|
process.kill(process.pid, exitCaught.name);
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2014-09-19 16:45:46 -07:00
|
|
|
return self._exit(exitCaught);
|
2014-09-17 14:21:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (errorCaught !== none) {
|
2014-09-18 14:54:08 -07:00
|
|
|
if (errorCaught && errorCaught.stack) {
|
2014-09-17 14:21:28 -07:00
|
|
|
console.error(errorCaught.stack);
|
|
|
|
}
|
|
|
|
return self._exit(0);
|
|
|
|
}
|
2014-09-17 12:52:35 -07:00
|
|
|
}
|
2014-09-17 14:08:26 -07:00
|
|
|
}, 1000);
|
2014-09-10 16:57:18 -07:00
|
|
|
|
2014-09-22 18:21:08 -07:00
|
|
|
this.pollInterval = 300;
|
|
|
|
|
2014-09-22 18:34:38 -07:00
|
|
|
this._emitted = {};
|
|
|
|
|
2014-09-22 17:58:59 -07:00
|
|
|
(function next() {
|
2014-09-23 10:21:44 -07:00
|
|
|
return bitcoindjs.pollBlocks(function(err, blocks) {
|
2014-09-22 18:21:08 -07:00
|
|
|
if (err) return setTimeout(next, self.pollInterval);
|
2014-09-23 10:21:44 -07:00
|
|
|
return utils.forEach(blocks, function(block, nextBlock) {
|
2014-09-22 18:34:38 -07:00
|
|
|
// XXX Bad workaround
|
2014-09-23 10:21:44 -07:00
|
|
|
if (self._emitted[block.hash]) {
|
|
|
|
return setImmediate(function() {
|
|
|
|
return nextBlock();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
self._emitted[block.hash] = true;
|
|
|
|
|
2014-09-22 17:58:59 -07:00
|
|
|
self.emit('block', block);
|
2014-09-23 10:21:44 -07:00
|
|
|
|
2014-09-23 11:17:25 -07:00
|
|
|
return utils.forEach(block.tx, function(tx, nextTx) {
|
|
|
|
self.emit('tx', tx);
|
|
|
|
return setImmediate(function() {
|
|
|
|
return nextTx();
|
|
|
|
});
|
|
|
|
}, function() {
|
|
|
|
return setImmediate(function() {
|
|
|
|
return nextBlock();
|
|
|
|
});
|
2014-09-22 18:21:08 -07:00
|
|
|
});
|
|
|
|
}, function() {
|
2014-09-23 10:21:44 -07:00
|
|
|
return setTimeout(next, self.pollInterval);
|
2014-09-22 17:58:59 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
})();
|
2014-09-22 16:36:36 -07:00
|
|
|
|
2014-09-17 15:33:21 -07:00
|
|
|
if (this.log_pipe !== -1) {
|
|
|
|
this.log('log pipe opened: %d', this.log_pipe);
|
|
|
|
this._pipe = new net.Socket(this.log_pipe);
|
|
|
|
this._pipe.on('data', function(data) {
|
|
|
|
return process.stdout.write('bitcoind: ' + data + '\n');
|
|
|
|
});
|
|
|
|
this._pipe.on('error', function(err) {
|
|
|
|
; // ignore for now
|
|
|
|
});
|
|
|
|
this._pipe.resume();
|
|
|
|
}
|
2014-09-11 17:18:36 -07:00
|
|
|
};
|
2014-08-12 12:03:04 -07:00
|
|
|
|
2014-09-22 12:27:33 -07:00
|
|
|
Bitcoin.prototype.getBlock = function(blockHash, callback) {
|
|
|
|
return bitcoindjs.getBlock(blockHash, callback);
|
2014-09-18 17:14:17 -07:00
|
|
|
};
|
|
|
|
|
2014-09-22 12:27:33 -07:00
|
|
|
Bitcoin.prototype.getTx = function(txHash, blockHash, callback) {
|
|
|
|
if (!callback) {
|
|
|
|
callback = blockHash;
|
|
|
|
blockHash = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (txHash[1] === 'x') txHash = txHash.slice(2);
|
|
|
|
// txHash = utils.revHex(txHash);
|
|
|
|
|
|
|
|
// if (blockHash) {
|
|
|
|
// if (blockHash[1] === 'x') blockHash = blockHash.slice(2);
|
|
|
|
// blockHash = utils.revHex(blockHash);
|
|
|
|
// }
|
|
|
|
|
|
|
|
return bitcoindjs.getTx(txHash, blockHash, callback);
|
2014-09-22 12:05:17 -07:00
|
|
|
};
|
|
|
|
|
2014-09-02 19:28:20 -07:00
|
|
|
Bitcoin.prototype.log =
|
|
|
|
Bitcoin.prototype.info = function() {
|
|
|
|
if (typeof arguments[0] !== 'string') {
|
|
|
|
var out = util.inspect(arguments[0], null, 20, true);
|
2014-09-17 14:31:20 -07:00
|
|
|
return process.stdout.write('bitcoind.js: ' + out + '\n');
|
2014-09-02 19:28:20 -07:00
|
|
|
}
|
|
|
|
var out = util.format.apply(util, arguments);
|
2014-09-17 14:31:20 -07:00
|
|
|
return process.stdout.write('bitcoind.js: ' + out + '\n');
|
2014-09-02 19:28:20 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
Bitcoin.prototype.error = function() {
|
|
|
|
if (typeof arguments[0] !== 'string') {
|
|
|
|
var out = util.inspect(arguments[0], null, 20, true);
|
2014-09-17 14:31:20 -07:00
|
|
|
return process.stderr.write('bitcoind.js: ' + out + '\n');
|
2014-09-02 19:28:20 -07:00
|
|
|
}
|
|
|
|
var out = util.format.apply(util, arguments);
|
2014-09-17 14:31:20 -07:00
|
|
|
return process.stderr.write('bitcoind.js: ' + out + '\n');
|
2014-09-02 19:28:20 -07:00
|
|
|
};
|
|
|
|
|
2014-09-11 17:18:36 -07:00
|
|
|
Bitcoin.prototype.stop =
|
|
|
|
Bitcoin.prototype.close = function(callback) {
|
|
|
|
var self = this;
|
|
|
|
return bitcoindjs.stop(function(err, status) {
|
|
|
|
if (err) {
|
|
|
|
self.error(err.message);
|
|
|
|
} else {
|
|
|
|
self.log(status);
|
|
|
|
}
|
|
|
|
if (!callback) return;
|
|
|
|
return callback(err, status);
|
|
|
|
});
|
2014-09-10 16:57:18 -07:00
|
|
|
};
|
2014-09-02 19:28:20 -07:00
|
|
|
|
2014-09-22 12:14:51 -07:00
|
|
|
/**
|
|
|
|
* Utils
|
|
|
|
*/
|
|
|
|
|
|
|
|
var utils = {};
|
|
|
|
|
|
|
|
utils.revHex = function revHex(s) {
|
|
|
|
var r = '';
|
|
|
|
for (var i = 0; i < s.length; i += 2) {
|
|
|
|
r = s.slice(i, i + 2) + r;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
};
|
|
|
|
|
2014-09-22 18:21:08 -07:00
|
|
|
utils.forEach = function(obj, iter, done) {
|
|
|
|
var pending = obj.length;
|
|
|
|
if (!pending) return done();
|
|
|
|
var next = function() {
|
|
|
|
if (!--pending) done();
|
|
|
|
};
|
|
|
|
obj.forEach(function(item) {
|
|
|
|
iter(item, next);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* Expose
|
|
|
|
*/
|
|
|
|
|
|
|
|
module.exports = exports = Bitcoin;
|
|
|
|
exports.Bitcoin = Bitcoin;
|
|
|
|
exports.native = bitcoindjs;
|
2014-09-22 12:14:51 -07:00
|
|
|
exports.utils = utils;
|