diff --git a/.gitignore b/.gitignore index e94d8aba..8132c320 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,7 @@ coverage/* **/*.creator *.log .DS_Store -bin/bitcoin* \ No newline at end of file +bin/bitcoin* +regtest/data/node1/regtest +regtest/data/node2/regtest +regtest/data/node3/regtest diff --git a/.travis.yml b/.travis.yml index 1192bff0..06a1ff97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ node_js: script: - _mocha -R spec regtest/p2p.js - _mocha -R spec regtest/bitcoind.js + - _mocha -R spec regtest/cluster.js - _mocha -R spec regtest/node.js - _mocha -R spec --recursive diff --git a/lib/services/bitcoind.js b/lib/services/bitcoind.js index f2e5c89a..220a8e53 100644 --- a/lib/services/bitcoind.js +++ b/lib/services/bitcoind.js @@ -111,7 +111,8 @@ Bitcoin.prototype.getAPIMethods = function() { ['getAddressBalance', this, this.getAddressBalance, 2], ['getAddressUnspentOutputs', this, this.getAddressUnspentOutputs, 2], ['getAddressHistory', this, this.getAddressHistory, 2], - ['getAddressSummary', this, this.getAddressSummary, 1] + ['getAddressSummary', this, this.getAddressSummary, 1], + ['generateBlock', this, this.generateBlock, 1] ]; return methods; }; @@ -372,6 +373,18 @@ Bitcoin.prototype._initZmqSubSocket = function(node, zmqUrl) { var self = this; node.zmqSubSocket = zmq.socket('sub'); + node.zmqSubSocket.on('connect', function(fd, endPoint) { + log.info('ZMQ connected to:', endPoint); + }); + + node.zmqSubSocket.on('connect_delay', function(fd, endPoint) { + log.warn('ZMQ connection delay:', endPoint); + }); + + node.zmqSubSocket.on('disconnect', function(fd, endPoint) { + log.warn('ZMQ disconnect:', endPoint); + }); + node.zmqSubSocket.on('monitor_error', function(err) { log.error('Error in monitoring: %s, will restart monitoring in 5 seconds', err); setTimeout(function() { @@ -394,7 +407,6 @@ Bitcoin.prototype._checkReindex = function(node, callback) { log.info('Bitcoin Core Daemon Reindex Percentage: ' + percentSynced.toFixed(2)); if (Math.round(percentSynced) >= 100) { node._reindex = false; - self._subscribeZmqEvents(node); callback(); clearInterval(interval); } @@ -402,7 +414,6 @@ Bitcoin.prototype._checkReindex = function(node, callback) { }, self._reindexWait); } else { - self._subscribeZmqEvents(node); callback(); } }; @@ -481,6 +492,7 @@ Bitcoin.prototype._spawnChildProcess = function(callback) { if (err) { return callback(err); } + self._subscribeZmqEvents(node); callback(null, node); }); @@ -512,6 +524,7 @@ Bitcoin.prototype._connectProcess = function(config, callback) { } self._initZmqSubSocket(node, config.zmqpubrawtx); + self._subscribeZmqEvents(node); callback(null, node); }); @@ -1297,6 +1310,16 @@ Bitcoin.prototype.getInfo = function(callback) { }); }; +Bitcoin.prototype.generateBlock = function(num, callback) { + var self = this; + this.client.generate(num, function(err, response) { + if (err) { + return callback(self._wrapRPCError(err)); + } + callback(null, response.result); + }); +}; + /** * Called by Node to stop the service. * @param {Function} callback diff --git a/regtest/cluster.js b/regtest/cluster.js new file mode 100644 index 00000000..30db5377 --- /dev/null +++ b/regtest/cluster.js @@ -0,0 +1,184 @@ +'use strict'; + +var path = require('path'); +var async = require('async'); +var spawn = require('child_process').spawn; + +var BitcoinRPC = require('bitcoind-rpc'); +var rimraf = require('rimraf'); +var bitcore = require('bitcore-lib'); +var chai = require('chai'); +var should = chai.should(); + +var index = require('..'); +var log = index.log; +log.debug = function() {}; +var BitcoreNode = index.Node; +var BitcoinService = index.services.Bitcoin; + +describe('Bitcoin Cluster', function() { + var node; + var daemons = []; + var execPath = path.resolve(__dirname, '../bin/bitcoind'); + var nodesConf = [ + { + datadir: path.resolve(__dirname, './data/node1'), + conf: path.resolve(__dirname, './data/node1/bitcoin.conf'), + rpcuser: 'bitcoin', + rpcpassword: 'local321', + rpcport: 30521, + zmqpubrawtx: 'tcp://127.0.0.1:30611', + zmqpubhashblock: 'tcp://127.0.0.1:30611' + }, + { + datadir: path.resolve(__dirname, './data/node2'), + conf: path.resolve(__dirname, './data/node2/bitcoin.conf'), + rpcuser: 'bitcoin', + rpcpassword: 'local321', + rpcport: 30522, + zmqpubrawtx: 'tcp://127.0.0.1:30622', + zmqpubhashblock: 'tcp://127.0.0.1:30622' + }, + { + datadir: path.resolve(__dirname, './data/node3'), + conf: path.resolve(__dirname, './data/node3/bitcoin.conf'), + rpcuser: 'bitcoin', + rpcpassword: 'local321', + rpcport: 30523, + zmqpubrawtx: 'tcp://127.0.0.1:30633', + zmqpubhashblock: 'tcp://127.0.0.1:30633' + } + ]; + + before(function(done) { + log.info('Starting 3 bitcoind daemons'); + this.timeout(20000); + async.each(nodesConf, function(nodeConf, next) { + var opts = [ + '--regtest', + '--datadir=' + nodeConf.datadir, + '--conf=' + nodeConf.conf + ]; + + rimraf(path.resolve(nodeConf.datadir, './regtest'), function(err) { + if (err) { + return done(err); + } + + var process = spawn(execPath, opts, {stdio: 'inherit'}); + + var client = new BitcoinRPC({ + protocol: 'http', + host: '127.0.0.1', + port: nodeConf.rpcport, + user: nodeConf.rpcuser, + pass: nodeConf.rpcpassword + }); + + daemons.push(process); + + async.retry({times: 10, interval: 5000}, function(ready) { + client.getInfo(ready); + }, next); + + }); + + }, done); + }); + + after(function(done) { + this.timeout(10000); + setTimeout(function() { + async.each(daemons, function(process, next) { + process.once('exit', next); + process.kill('SIGINT'); + }, done); + }, 1000); + }); + + it('step 1: will connect to three bitcoind daemons', function(done) { + this.timeout(20000); + var configuration = { + network: 'regtest', + services: [ + { + name: 'bitcoind', + module: BitcoinService, + config: { + connect: [ + { + rpchost: '127.0.0.1', + rpcport: 30521, + rpcuser: 'bitcoin', + rpcpassword: 'local321', + zmqpubrawtx: 'tcp://127.0.0.1:30611' + }, + { + rpchost: '127.0.0.1', + rpcport: 30522, + rpcuser: 'bitcoin', + rpcpassword: 'local321', + zmqpubrawtx: 'tcp://127.0.0.1:30622' + }, + { + rpchost: '127.0.0.1', + rpcport: 30523, + rpcuser: 'bitcoin', + rpcpassword: 'local321', + zmqpubrawtx: 'tcp://127.0.0.1:30633' + } + ] + } + } + ] + }; + + var regtest = bitcore.Networks.get('regtest'); + should.exist(regtest); + + node = new BitcoreNode(configuration); + + node.on('error', function(err) { + log.error(err); + }); + + node.on('ready', function() { + done(); + }); + + node.start(function(err) { + if (err) { + return done(err); + } + }); + + }); + + it('step 2: receive block events', function(done) { + this.timeout(10000); + node.services.bitcoind.once('tip', function() { + setTimeout(function() { + done(); + }, 1000); + }); + node.generateBlock(1, function(err, hashes) { + if (err) { + return done(err); + } + should.exist(hashes); + }); + }); + + it('step 3: get blocks', function(done) { + async.times(3, function(n, next) { + node.getBlock(1, function(err, block) { + if (err) { + return next(err); + } + should.exist(block); + next(); + }); + }, done); + }); + +}); diff --git a/regtest/data/node1/bitcoin.conf b/regtest/data/node1/bitcoin.conf new file mode 100644 index 00000000..695d6d3e --- /dev/null +++ b/regtest/data/node1/bitcoin.conf @@ -0,0 +1,14 @@ +server=1 +whitelist=127.0.0.1 +txindex=1 +addressindex=1 +timestampindex=1 +addnode=127.0.0.1:30432 +addnode=127.0.0.1:30433 +port=30431 +rpcport=30521 +zmqpubrawtx=tcp://127.0.0.1:30611 +zmqpubhashblock=tcp://127.0.0.1:30611 +rpcallowip=127.0.0.1 +rpcuser=bitcoin +rpcpassword=local321 diff --git a/regtest/data/node2/bitcoin.conf b/regtest/data/node2/bitcoin.conf new file mode 100644 index 00000000..74d898af --- /dev/null +++ b/regtest/data/node2/bitcoin.conf @@ -0,0 +1,14 @@ +server=1 +whitelist=127.0.0.1 +txindex=1 +addressindex=1 +timestampindex=1 +addnode=127.0.0.1:30431 +addnode=127.0.0.1:30433 +port=30432 +rpcport=30522 +zmqpubrawtx=tcp://127.0.0.1:30622 +zmqpubhashblock=tcp://127.0.0.1:30622 +rpcallowip=127.0.0.1 +rpcuser=bitcoin +rpcpassword=local321 diff --git a/regtest/data/node3/bitcoin.conf b/regtest/data/node3/bitcoin.conf new file mode 100644 index 00000000..0edb5bee --- /dev/null +++ b/regtest/data/node3/bitcoin.conf @@ -0,0 +1,14 @@ +server=1 +whitelist=127.0.0.1 +txindex=1 +addressindex=1 +timestampindex=1 +addnode=127.0.0.1:30431 +addnode=127.0.0.1:30432 +port=30433 +rpcport=30523 +zmqpubrawtx=tcp://127.0.0.1:30633 +zmqpubhashblock=tcp://127.0.0.1:30633 +rpcallowip=127.0.0.1 +rpcuser=bitcoin +rpcpassword=local321