bitcore-node-zcash/lib/bitcoind.js

558 lines
12 KiB
JavaScript
Raw Normal View History

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-09-25 10:59:36 -07:00
var bn = require('bn.js');
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;
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) {
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) {
if (process.listeners('uncaughtException').length > 1) {
return;
}
2014-09-17 14:21:28 -07:00
errorCaught = err;
if (!self._shutdown) {
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) {
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-23 13:57:49 -07:00
this.on('newListener', function(name) {
if (name === 'block') {
self._pollBlocks();
return;
}
if (name === 'tx') {
self._pollBlocks();
self._pollMempool();
return;
}
if (name === 'mptx') {
self._pollMempool();
return;
}
});
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();
}
};
Bitcoin.prototype._pollBlocks = function() {
2014-09-23 14:01:10 -07:00
var self = this;
2014-09-23 13:57:49 -07:00
if (this._pollingBlocks) return;
this._pollingBlocks = true;
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-23 13:57:49 -07:00
};
2014-09-22 16:36:36 -07:00
2014-09-23 13:57:49 -07:00
Bitcoin.prototype._pollMempool = function() {
2014-09-23 14:01:10 -07:00
var self = this;
2014-09-23 13:57:49 -07:00
if (this._pollingMempool) return;
this._pollingMempool = true;
(function next() {
return bitcoindjs.pollMempool(function(err, txs) {
if (err) return setTimeout(next, self.pollInterval);
return utils.forEach(txs, function(tx, nextTx) {
// XXX Bad workaround
if (self._emitted[tx.hash]) {
return setImmediate(function() {
return nextTx();
});
}
self._emitted[tx.hash] = true;
self.emit('mptx', tx);
self.emit('tx', tx);
return setImmediate(function() {
return nextTx();
});
}, function() {
return setTimeout(next, self.pollInterval);
});
2014-09-17 15:33:21 -07:00
});
2014-09-23 13:57:49 -07:00
})();
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-25 10:59:36 -07:00
/**
* Block
*/
function Block(data) {
if (!(this instanceof Block)) {
return new Block(data);
}
}
/**
* Transaction
*/
function Transaction(data) {
if (!(this instanceof Transaction)) {
return new Transaction(data);
}
2014-09-25 13:17:07 -07:00
this.nMinTxFee = data.nMinTxFee || data.minTxFee || 1000;
this.nMinRelayTxFee = data.nMinRelayTxFee || data.minRelayTxFee || 1000;
2014-09-25 10:59:36 -07:00
this.CURRENT_VERSION = 1;
2014-09-25 13:17:07 -07:00
this.nVersion = data.nVersion || data.version || this.CURRENT_VERSION;
2014-09-25 10:59:36 -07:00
this.vin = data.vin || [];
this.vout = data.vout || [];
2014-09-25 13:17:07 -07:00
this.nLockTime = data.nLockTime || data.locktime || 0;
2014-09-25 10:59:36 -07:00
}
Transaction.prototype.getSerializeSize = function() {
;
};
Transaction.prototype.serialize = function() {
;
};
Transaction.prototype.unserialize = function() {
;
};
Transaction.prototype.setNull = function() {
;
};
Transaction.prototype.isNull = function() {
;
};
Transaction.prototype.getHash = function() {
;
};
Transaction.prototype.getValueOut = function() {
;
};
Transaction.prototype.computePriority = function() {
;
};
Transaction.prototype.isCoinbase = function() {
;
};
Transaction.prototype.equal = function() {
;
};
Transaction.prototype.notEqual = function() {
;
};
Transaction.prototype.toString = function() {
;
};
Transaction.prototype.print = function() {
;
};
2014-09-25 12:05:39 -07:00
Transaction.prototype.toHex = function() {
return this.hex = this.hex || Transaction.toHex(this);
};
Transaction.toHex = function(tx) {
2014-09-25 13:17:07 -07:00
return new bn(Transaction.toBinary(tx)).toString('hex');
2014-09-25 12:05:39 -07:00
};
/**
* Broadcast TX
*/
2014-09-25 13:12:28 -07:00
Bitcoin._broadcastTx =
Bitcoin.prototype._broadcastTx = function(tx, options, callback) {
if (typeof tx === 'string') {
tx = { hex: tx };
}
2014-09-25 12:05:39 -07:00
if (!callback) {
callback = options;
options = null;
}
if (!options) {
options = {};
}
options.overrideFees = options.overrideFees || false;
2014-09-25 13:12:28 -07:00
options.ownOnly = options.ownOnly || false;
2014-09-25 12:05:39 -07:00
2014-09-25 13:12:28 -07:00
return bitcoindjs.broadcastTx(tx,
options.overrideFees,
options.ownOnly,
callback);
2014-09-25 12:05:39 -07:00
};
2014-09-25 13:17:07 -07:00
Transaction.toBinary = function(tx) {
2014-09-25 12:05:39 -07:00
var p = [];
2014-09-25 13:12:28 -07:00
var off = utils.writeU32(p, tx.nVersion || tx.version, 0);
2014-09-25 12:05:39 -07:00
off += utils.varint(p, tx.vin.length, off);
for (var i = 0; i < tx.vin.length; i++) {
var input = tx.vin[i];
2014-09-25 13:12:28 -07:00
if (input.coinbase) {
off += utils.copy(new bn(input.coinbase, 'hex').toArray(), p, off, true);
off += utils.writeU32(p, input.sequence, off);
} else {
off += utils.copy(new bn(input.txid, 'hex').toArray(), p, off, true);
off += utils.writeU32(p, input.vout, off);
2014-09-25 12:05:39 -07:00
2014-09-25 13:12:28 -07:00
var s = script.encode(new bn(input.scriptSig.hex, 'hex').toArray());
off += utils.varint(p, s.length, off);
off += utils.copy(s, p, off, true);
2014-09-25 12:05:39 -07:00
2014-09-25 13:12:28 -07:00
off += utils.writeU32(p, input.sequence, off);
}
2014-09-25 12:05:39 -07:00
}
off += utils.varint(p, tx.vout.length, off);
for (var i = 0; i < tx.vout.length; i++) {
var output = tx.vout[i];
// Put LE value
2014-09-25 13:12:28 -07:00
var value = new bn(output.value).toArray().slice().reverse();
2014-09-25 12:05:39 -07:00
assert(value.length <= 8);
off += utils.copy(value, p, off, true);
2014-09-25 13:12:28 -07:00
for (var j = value.length; j < 8; j++, off++) {
2014-09-25 12:05:39 -07:00
p[off] = 0;
2014-09-25 13:12:28 -07:00
}
2014-09-25 12:05:39 -07:00
2014-09-25 13:12:28 -07:00
var s = script.encode(new bn(output.scriptPubKey.hex, 'hex').toArray());
2014-09-25 12:05:39 -07:00
off += utils.varint(p, s.length, off);
off += utils.copy(s, p, off, true);
}
2014-09-25 13:12:28 -07:00
off += utils.writeU32(p, tx.nLockTime || tx.locktime, off);
2014-09-25 12:05:39 -07:00
return p;
};
var script = {};
script.encode = function encode(s) {
2014-09-25 13:12:28 -07:00
if (!s) {
2014-09-25 12:05:39 -07:00
return [];
2014-09-25 13:12:28 -07:00
}
2014-09-25 12:05:39 -07:00
var res = [];
for (var i = 0; i < s.length; i++) {
var instr = s[i];
// Push value to stack
if (Array.isArray(instr)) {
if (instr.length === 0) {
res.push(0);
} else if (instr.length === 1 && 0 < instr[0] && instr[0] <= 16) {
res.push(0x50 + instr[0]);
} else if (1 <= instr.length && instr.length <= 0x4b) {
res = res.concat(instr.length, instr);
} else if (instr.length <= 0xff) {
2014-09-25 13:12:28 -07:00
res = res.concat(0x4c, instr.length, instr);
2014-09-25 12:05:39 -07:00
} else if (instr.length <= 0xffff) {
2014-09-25 13:12:28 -07:00
res.push(0x4d);
2014-09-25 12:05:39 -07:00
utils.writeU16(res, instr.length, res.length);
res = res.concat(instr);
} else {
2014-09-25 13:12:28 -07:00
res.push(0x4e);
2014-09-25 12:05:39 -07:00
utils.writeU32(res, instr.length, res.length);
res = res.concat(instr);
}
continue;
}
2014-09-25 13:12:28 -07:00
res.push(instr);
2014-09-25 12:05:39 -07:00
}
return res;
};
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-09-25 12:05:39 -07:00
utils.writeU16 = function writeU16(dst, num, off) {
if (!off)
off = 0;
dst[off] = num & 0xff;
dst[off + 1] = (num >>> 8) & 0xff;
return 2;
};
utils.writeU32 = function writeU32(dst, num, off) {
if (!off)
off = 0;
dst[off] = num & 0xff;
dst[off + 1] = (num >>> 8) & 0xff;
dst[off + 2] = (num >>> 16) & 0xff;
dst[off + 3] = (num >>> 24) & 0xff;
return 4;
};
utils.varint = function(arr, value, off) {
if (!off)
off = 0;
if (value < 0xfd) {
arr[off] = value;
return 1;
} else if (value <= 0xffff) {
arr[off] = 0xfd;
arr[off + 1] = value & 0xff;
arr[off + 2] = value >>> 8;
return 3;
} else if (value <= 0xffffffff) {
arr[off] = 0xfe;
arr[off + 1] = value & 0xff;
arr[off + 2] = (value >>> 8) & 0xff;
arr[off + 3] = (value >>> 16) & 0xff;
arr[off + 4] = value >>> 24;
return 5;
} else {
arr[off] = 0xff;
utils.writeU64(arr, value, off + 1);
return 9;
}
};
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;