diff --git a/Dockerfile b/Dockerfile index c2bf9e80..1fc344f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,4 +37,4 @@ RUN $PKG_NAME install @owstack/ows-explorer@0.0.3 RUN $PKG_NAME install @owstack/ows-elastic-sync@1.0.3 USER root -CMD ["btcnode","start"] +CMD ["btcnode", "start", "-c", "/home/ows/config", "-m", "/home/ows/bitcoin-core-services"] diff --git a/docker-compose.yml b/docker-compose.yml index 1adb18c4..fda24669 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,5 @@ version: '3.2' services: - bitcoin-core-conf: - build: extra/compose - volumes: - - bitcoin-core-data-dir:/data - - btc-conf-dir:/home/ows/config bitcoin-core: image: "owstack/bitcoin-core:0.14.1-ows" user: root @@ -17,9 +12,7 @@ services: - "28332:28332" - "28333:28333" volumes: - - bitcoin-core-data-dir:/data - depends_on: - - "bitcoin-core-conf" + - /data/bitcoin-core:/data btc-node: build: . ports: @@ -27,8 +20,5 @@ services: depends_on: - "bitcoin-core" volumes: - - btc-conf-dir:/home/ows/config - command: "btcnode start -c /home/ows/config" -volumes: - bitcoin-core-data-dir: - btc-conf-dir: + - /data/btc-node:/home/ows/config + command: "btcnode start -c /home/ows/config -m /home/ows/bitcoin-core-services" diff --git a/docs/development.md b/docs/development.md index 9cd7965c..781045f1 100644 --- a/docs/development.md +++ b/docs/development.md @@ -120,10 +120,16 @@ Edit `btc-node.json` with something similar to: ], "servicesConfig": { "bitcoind": { - "spawn": { - "datadir": "/home//.bitcoin", - "exec": "/home//bitcoin/src/bitcoind" - } + "connect": [{ + "zmqpubrawtx": "tcp://bitcoin-core:28332", + "zmqpubhashblock": "tcp://bitcoin-core:28332", + "rpcprotocol": "http", + "rpchost": "bitcoin-core", + "rpcport": 8332, + "rpcuser": "bitcoin", + "rpcpassword": "local321" + }] + }, "explorer-api": { "module": "btc-explorer-api" @@ -162,4 +168,4 @@ rpcpassword=local321 From within the `devnode` directory with the configuration file, start the node: ```bash ../btc-node/bin/btc-node start -``` \ No newline at end of file +``` diff --git a/docs/services.md b/docs/services.md index 450a4820..790aad72 100644 --- a/docs/services.md +++ b/docs/services.md @@ -47,10 +47,15 @@ var myNode = new btc.Node({ name: 'bitcoind', module: Bitcoin, config: { - spawn: { - datadir: '/home//.bitcoin', - exec: '/home//btc-node/bin/bitcoind' - } + "connect": [{ + "zmqpubrawtx": "tcp://bitcoin-core:28332", + "zmqpubhashblock": "tcp://bitcoin-core:28332", + "rpcprotocol": "http", + "rpchost": "bitcoin-core", + "rpcport": 8332, + "rpcuser": "bitcoin", + "rpcpassword": "local321" + }] } }, { @@ -85,4 +90,3 @@ A new service can be created by inheriting from `Node.Service` and implementing The `package.json` for the service module can either export the `Node.Service` directly, or specify a specific module to load by including `"btcNode": "lib/btc-node.js"`. Please take a look at some of the existing services for implementation specifics. - diff --git a/docs/services/bitcoind.md b/docs/services/bitcoind.md index 9a89e7ac..2f3ab2d3 100644 --- a/docs/services/bitcoind.md +++ b/docs/services/bitcoind.md @@ -4,20 +4,7 @@ The Bitcoin Service is a Node.js interface to [Bitcoin Core](https://github.com/ ## Configuration -The default configuration will include a "spawn" configuration in "bitcoind". This defines the location of the block chain database and the location of the `bitcoind` daemon executable. The below configuration points to a local clone of `bitcoin`, and will start `bitcoind` automatically with your Node.js application. - -```json - "servicesConfig": { - "bitcoind": { - "spawn": { - "datadir": "/home/btc/.bitcoin", - "exec": "/home/btc/bitcoin/src/bitcoind" - } - } - } -``` - -It's also possible to connect to separately managed `bitcoind` processes with round-robin quering, for example: +It is possible to connect to separately managed `bitcoind` processes with round-robin quering, for example: ```json "servicesConfig": { diff --git a/extra/compose/Dockerfile b/extra/compose/Dockerfile deleted file mode 100644 index 3aa4a3c0..00000000 --- a/extra/compose/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM buildpack-deps:jessie -MAINTAINER Ian Patton (ian.patton@gmail.com) - -ENV BITCOIN_DATA=/data -ENV CONFIG_DIR=/home/ows/config - -RUN mkdir -p $BITCOIN_DATA && mkdir -p $CONFIG_DIR - -COPY default.bitcoin.conf $BITCOIN_DATA/bitcoin.conf -COPY default.btc-node.json $CONFIG_DIR/btc-node.json -COPY default.btc-node.conf $CONFIG_DIR/bitcoin.conf - -CMD ["echo","installed config files"] diff --git a/extra/compose/default.bitcoin.conf b/extra/compose/default.bitcoin.conf deleted file mode 100644 index c57b8c46..00000000 --- a/extra/compose/default.bitcoin.conf +++ /dev/null @@ -1,11 +0,0 @@ -server=1 -txindex=1 -addressindex=1 -timestampindex=1 -spentindex=1 -zmqpubrawtx=tcp://0.0.0.0:28332 -zmqpubhashblock=tcp://0.0.0.0:28332 -rpcallowip=0.0.0.0/0 -rpcuser=bitcoin -rpcpassword=local321 -uacomment=bcccore diff --git a/extra/compose/default.btc-node.conf b/extra/compose/default.btc-node.conf deleted file mode 100644 index 1c0866dd..00000000 --- a/extra/compose/default.btc-node.conf +++ /dev/null @@ -1,13 +0,0 @@ -server=1 -txindex=1 -addressindex=1 -timestampindex=1 -spentindex=1 -zmqpubrawtx=tcp://bitcoin-core:28332 -zmqpubhashblock=tcp://bitcoin-core:28332 -rpcallowip=0.0.0.0/0 -rpcprotocol=http -rpchost=bitcoin-core -rpcuser=bitcoin -rpcpassword=local321 -uacomment=bcccore diff --git a/extra/compose/default.btc-node.json b/extra/compose/default.btc-node.json deleted file mode 100644 index 72416e4f..00000000 --- a/extra/compose/default.btc-node.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "network": "livenet", - "port": 3001, - "services": [ - "explorer-api", - "bitcoind", - "web" - ], - "servicesConfig": { - "bitcoind": { - "spawn": { - "datadir": "/home/ows/config", - "exec": "docker" - }, - "connect": { - "rpcprotocol": "http", - "rpchost": "bitcoin-core", - "rpcuser": "bitcoin", - "rpcpassword": "local321", - "rpcstrict": true - } - }, - "explorer-api": { - "module": "btc-explorer-api", - "apiPrefix": "explorer-api" - } - } -} diff --git a/lib/scaffold/default-base-config.js b/lib/scaffold/default-base-config.js index 36de247d..5e6e58b0 100644 --- a/lib/scaffold/default-base-config.js +++ b/lib/scaffold/default-base-config.js @@ -1,13 +1,10 @@ 'use strict'; -var path = require('path'); - /** * Will return the path and default btc-node configuration on environment variables * or default locations. * @param {Object} options - * @param {String} options.network - "testnet" or "livenet" - * @param {String} options.datadir - Absolute path to bitcoin database directory + * @param {String} options.network - testnet or livenet */ function getDefaultBaseConfig(options) { if (!options) { @@ -21,10 +18,15 @@ function getDefaultBaseConfig(options) { services: ['bitcoind', 'web'], servicesConfig: { bitcoind: { - spawn: { - datadir: options.datadir || path.resolve(process.env.HOME, '.bitcoin'), - exec: path.resolve(__dirname, '../../bin/bitcoind') - } + connect: [{ + zmqpubrawtx: 'tcp://bitcoin-core:28332', + zmqpubhashblock: 'tcp://bitcoin-core:28332', + rpcprotocol: 'http', + rpchost: 'bitcoin-core', + rpcport: 8332, + rpcuser: 'bitcoin', + rpcpassword: 'local321' + }] } } } diff --git a/lib/scaffold/default-config.js b/lib/scaffold/default-config.js index a243ff18..ffe1ace0 100644 --- a/lib/scaffold/default-config.js +++ b/lib/scaffold/default-config.js @@ -6,7 +6,7 @@ var fs = require('fs'); /** * Will return the path and default btc-node configuration. It will search for the - * configuration file in the "~/.btc" directory, and if it doesn't exist, it will create one + * configuration file in the ~/.btc directory, and if it doesn't exist, it will create one * based on default settings. * @param {Object} [options] * @param {Array} [options.additionalServices] - An optional array of services. @@ -36,10 +36,15 @@ function getDefaultConfig(options) { services: defaultServices, servicesConfig: { bitcoind: { - spawn: { - datadir: path.resolve(defaultPath, './data'), - exec: path.resolve(__dirname, '../../bin/bitcoind') - } + connect: [{ + zmqpubrawtx: 'tcp://bitcoin-core:28332', + zmqpubhashblock: 'tcp://bitcoin-core:28332', + rpcprotocol: 'http', + rpchost: 'bitcoin-core', + rpcport: 8332, + rpcuser: 'bitcoin', + rpcpassword: 'local321' + }] } } }; diff --git a/lib/scaffold/start.js b/lib/scaffold/start.js index 02807ff0..539d1b19 100644 --- a/lib/scaffold/start.js +++ b/lib/scaffold/start.js @@ -32,10 +32,15 @@ function checkConfigVersion2(fullConfig) { var missingConfig = { servicesConfig: { bitcoind: { - spawn: { - datadir: fullConfig.datadir, - exec: path.resolve(__dirname, '../../bin/bitcoind') - } + connect: [{ + zmqpubrawtx: 'tcp://bitcoin-core:28332', + zmqpubhashblock: 'tcp://bitcoin-core:28332', + rpcprotocol: 'http', + rpchost: 'bitcoin-core', + rpcport: 8332, + rpcuser: 'bitcoin', + rpcpassword: 'local321' + }] } } }; diff --git a/lib/services/bitcoind.js b/lib/services/bitcoind.js index b09472d9..68c02c91 100644 --- a/lib/services/bitcoind.js +++ b/lib/services/bitcoind.js @@ -1,9 +1,7 @@ 'use strict'; var fs = require('fs'); -var path = require('path'); var util = require('util'); -var mkdirp = require('mkdirp'); var btcLib = require('@owstack/btc-lib'); var zmq = require('zeromq'); var async = require('async'); @@ -16,7 +14,6 @@ var Transaction = btcLib.Transaction; var index = require('../'); var errors = index.errors; var log = index.log; -var utils = require('../utils'); var Service = require('../service'); /** @@ -38,9 +35,6 @@ function Bitcoin(options) { this._initCaches(); - // bitcoind child process - this.spawn = false; - // event subscribers this.subscriptions = {}; this.subscriptions.rawtransaction = []; @@ -69,8 +63,6 @@ Bitcoin.DEFAULT_MAX_HISTORY = 50; Bitcoin.DEFAULT_SHUTDOWN_TIMEOUT = 15000; Bitcoin.DEFAULT_ZMQ_SUBSCRIBE_PROGRESS = 0.9999; Bitcoin.DEFAULT_MAX_ADDRESSES_QUERY = 10000; -Bitcoin.DEFAULT_SPAWN_RESTART_TIME = 5000; -Bitcoin.DEFAULT_SPAWN_STOP_TIME = 10000; Bitcoin.DEFAULT_TRY_ALL_INTERVAL = 1000; Bitcoin.DEFAULT_REINDEX_INTERVAL = 10000; Bitcoin.DEFAULT_START_RETRY_INTERVAL = 5000; @@ -78,14 +70,12 @@ Bitcoin.DEFAULT_TIP_UPDATE_INTERVAL = 15000; Bitcoin.DEFAULT_TRANSACTION_CONCURRENCY = 5; Bitcoin.DEFAULT_CONFIG_SETTINGS = { server: 1, - whitelist: '127.0.0.1', txindex: 1, addressindex: 1, timestampindex: 1, spentindex: 1, - zmqpubrawtx: 'tcp://127.0.0.1:28332', - zmqpubhashblock: 'tcp://127.0.0.1:28332', - rpcallowip: '127.0.0.1', + zmqpubrawtx: 'tcp://0.0.0.0:28332', + zmqpubhashblock: 'tcp://0.0.0.0:28332', rpcuser: 'bitcoin', rpcpassword: 'local321', uacomment: 'btc' @@ -100,10 +90,6 @@ Bitcoin.prototype._initDefaults = function(options) { this.maxAddressesQuery = options.maxAddressesQuery || Bitcoin.DEFAULT_MAX_ADDRESSES_QUERY; this.shutdownTimeout = options.shutdownTimeout || Bitcoin.DEFAULT_SHUTDOWN_TIMEOUT; - // spawn restart setting - this.spawnRestartTime = options.spawnRestartTime || Bitcoin.DEFAULT_SPAWN_RESTART_TIME; - this.spawnStopTime = options.spawnStopTime || Bitcoin.DEFAULT_SPAWN_STOP_TIME; - // try all interval this.tryAllInterval = options.tryAllInterval || Bitcoin.DEFAULT_TRY_ALL_INTERVAL; this.startRetryInterval = options.startRetryInterval || Bitcoin.DEFAULT_START_RETRY_INTERVAL; @@ -318,112 +304,6 @@ Bitcoin.prototype._parseBitcoinConf = function(configPath) { return options; }; -Bitcoin.prototype._expandRelativeDatadir = function() { - if (!utils.isAbsolutePath(this.options.spawn.datadir)) { - $.checkState(this.node.configPath); - $.checkState(utils.isAbsolutePath(this.node.configPath)); - var baseConfigPath = path.dirname(this.node.configPath); - this.options.spawn.datadir = path.resolve(baseConfigPath, this.options.spawn.datadir); - } -}; - -Bitcoin.prototype._loadSpawnConfiguration = function(node) { - /* jshint maxstatements: 25 */ - - $.checkArgument(this.options.spawn, 'Please specify "spawn" in bitcoind config options'); - $.checkArgument(this.options.spawn.datadir, 'Please specify "spawn.datadir" in bitcoind config options'); - $.checkArgument(this.options.spawn.exec, 'Please specify "spawn.exec" in bitcoind config options'); - - this._expandRelativeDatadir(); - - var spawnOptions = this.options.spawn; - var configPath = path.resolve(spawnOptions.datadir, './bitcoin.conf'); - - log.info('Using bitcoin config file:', configPath); - - this.spawn = {}; - this.spawn.datadir = this.options.spawn.datadir; - this.spawn.exec = this.options.spawn.exec; - this.spawn.configPath = configPath; - this.spawn.config = {}; - - if (!fs.existsSync(spawnOptions.datadir)) { - mkdirp.sync(spawnOptions.datadir); - } - - if (!fs.existsSync(configPath)) { - var defaultConfig = this._getDefaultConfig(); - fs.writeFileSync(configPath, defaultConfig); - } - - _.extend(this.spawn.config, this._getDefaultConf()); - _.extend(this.spawn.config, this._parseBitcoinConf(configPath)); - - var networkConfigPath = this._getNetworkConfigPath(); - if (networkConfigPath && fs.existsSync(networkConfigPath)) { - _.extend(this.spawn.config, this._parseBitcoinConf(networkConfigPath)); - } - - var spawnConfig = this.spawn.config; - - this._checkConfigIndexes(spawnConfig, node); - -}; - -Bitcoin.prototype._checkConfigIndexes = function(spawnConfig, node) { - $.checkState( - spawnConfig.txindex && spawnConfig.txindex === 1, - '"txindex" option is required in order to use transaction query features of btc-node. ' + - 'Please add "txindex=1" to your configuration and reindex an existing database if ' + - 'necessary with reindex=1' - ); - - $.checkState( - spawnConfig.addressindex && spawnConfig.addressindex === 1, - '"addressindex" option is required in order to use address query features of btc-node. ' + - 'Please add "addressindex=1" to your configuration and reindex an existing database if ' + - 'necessary with reindex=1' - ); - - $.checkState( - spawnConfig.spentindex && spawnConfig.spentindex === 1, - '"spentindex" option is required in order to use spent info query features of btc-node. ' + - 'Please add "spentindex=1" to your configuration and reindex an existing database if ' + - 'necessary with reindex=1' - ); - - $.checkState( - spawnConfig.server && spawnConfig.server === 1, - '"server" option is required to communicate to bitcoind from btc-node. ' + - 'Please add "server=1" to your configuration and restart' - ); - - $.checkState( - spawnConfig.zmqpubrawtx, - '"zmqpubrawtx" option is required to get event updates from bitcoind. ' + - 'Please add "zmqpubrawtx=tcp://127.0.0.1:" to your configuration and restart' - ); - - $.checkState( - spawnConfig.zmqpubhashblock, - '"zmqpubhashblock" option is required to get event updates from bitcoind. ' + - 'Please add "zmqpubhashblock=tcp://127.0.0.1:" to your configuration and restart' - ); - - $.checkState( - (spawnConfig.zmqpubhashblock === spawnConfig.zmqpubrawtx), - '"zmqpubrawtx" and "zmqpubhashblock" are expected to the same host and port in bitcoin.conf' - ); - - if (spawnConfig.reindex && spawnConfig.reindex === 1) { - log.warn('Reindex option is currently enabled. This means that bitcoind is undergoing a reindex. ' + - 'The reindex flag will start the index from beginning every time the node is started, so it ' + - 'should be removed after the reindex has been initiated. Once the reindex is complete, the rest ' + - 'of btc-node services will start.'); - node._reindex = true; - } -}; - Bitcoin.prototype._resetCaches = function() { this.transactionDetailedCache.reset(); this.utxosCache.reset(); @@ -789,107 +669,6 @@ Bitcoin.prototype._loadTipFromNode = function(node, callback) { }); }; -Bitcoin.prototype._stopSpawnedBitcoin = function(callback) { - var self = this; - var spawnOptions = this.options.spawn; - var pidPath = spawnOptions.datadir + '/bitcoind.pid'; - - function stopProcess() { - fs.readFile(pidPath, 'utf8', function(err, pid) { - if (err && err.code === 'ENOENT') { - // pid file doesn't exist we can continue - return callback(null); - } else if (err) { - return callback(err); - } - pid = parseInt(pid); - if (!Number.isFinite(pid)) { - // pid doesn't exist we can continue - return callback(null); - } - try { - log.warn('Stopping existing spawned bitcoin process with pid: ' + pid); - self._process.kill(pid, 'SIGINT'); - } catch(err) { - if (err && err.code === 'ESRCH') { - log.warn('Unclean bitcoin process shutdown, process not found with pid: ' + pid); - return callback(null); - } else if(err) { - return callback(err); - } - } - setTimeout(function() { - stopProcess(); - }, self.spawnStopTime); - }); - } - - stopProcess(); -}; - -Bitcoin.prototype._spawnChildProcess = function(callback) { - var self = this; - - var node = {}; - node._reindex = false; - node._reindexWait = 10000; - - try { - self._loadSpawnConfiguration(node); - } catch(e) { - return callback(e); - } - - var options = [ - '--conf=' + this.spawn.configPath, - '--datadir=' + this.spawn.datadir, - ]; - - if (self._getNetworkOption()) { - options.push(self._getNetworkOption()); - } - - var exitShutdown = false; - - async.retry({times: 60, interval: self.startRetryInterval}, function(done) { - if (self.node.stopping) { - exitShutdown = true; - return done(); - } - - node.client = new BitcoinRPC({ - protocol: self.spawn.config.rpcprotocol, - host: self.spawn.config.rpchost, - port: self.spawn.config.rpcport, - user: self.spawn.config.rpcuser, - pass: self.spawn.config.rpcpassword, - queue: self.spawn.config.rpcqueue - }); - - self._loadTipFromNode(node, done); - - }, function(err) { - if (err) { - return callback(err); - } - if (exitShutdown) { - return callback(new Error('Stopping while trying to spawn bitcoind.')); - } - - self._initZmqSubSocket(node, self.spawn.config.zmqpubrawtx); - - self._checkReindex(node, function(err) { - if (err) { - return callback(err); - } - self._checkSyncedAndSubscribeZmqEvents(node); - callback(null, node); - }); - - }); - -}; - Bitcoin.prototype._connectProcess = function(config, callback) { var self = this; var node = {}; @@ -934,46 +713,18 @@ Bitcoin.prototype._connectProcess = function(config, callback) { */ Bitcoin.prototype.start = function(callback) { var self = this; - - async.series([ - function(next) { - if (self.options.spawn) { - self._spawnChildProcess(function(err, node) { - if (err) { - return next(err); - } - self.nodes.push(node); - next(); - }); - } else { - next(); - } - }, - function(next) { - if (self.options.connect) { - async.map(self.options.connect, self._connectProcess.bind(self), function(err, nodes) { - if (err) { - return callback(err); - } - for(var i = 0; i < nodes.length; i++) { - self.nodes.push(nodes[i]); - } - next(); - }); - } else { - next(); - } - } - ], function(err) { + async.map(self.options.connect, self._connectProcess.bind(self), function(err, nodes) { if (err) { return callback(err); } + for(var i = 0; i < nodes.length; i++) { + self.nodes.push(nodes[i]); + } if (self.nodes.length === 0) { - return callback(new Error('Bitcoin configuration options "spawn" or "connect" are expected')); + return callback(new Error('Bitcoin configuration options for "connect" are expected')); } self._initChain(callback); }); - }; /** @@ -2084,30 +1835,7 @@ Bitcoin.prototype.generateBlock = function(num, callback) { * @param {Function} callback */ Bitcoin.prototype.stop = function(callback) { - if (this.spawn && this.spawn.process) { - var exited = false; - this.spawn.process.once('exit', function(code) { - if (!exited) { - exited = true; - if (code !== 0) { - var error = new Error('bitcoind spawned process exited with status code: ' + code); - error.code = code; - return callback(error); - } else { - return callback(); - } - } - }); - this.spawn.process.kill('SIGINT'); - setTimeout(function() { - if (!exited) { - exited = true; - return callback(new Error('bitcoind process did not exit')); - } - }, this.shutdownTimeout).unref(); - } else { - callback(); - } + callback(); }; module.exports = Bitcoin; diff --git a/test/scaffold/default-base-config.integration.js b/test/scaffold/default-base-config.integration.js index b622c742..b55a73b1 100644 --- a/test/scaffold/default-base-config.integration.js +++ b/test/scaffold/default-base-config.integration.js @@ -14,15 +14,10 @@ describe('#defaultBaseConfig', function() { info.config.port.should.equal(3001); info.config.services.should.deep.equal(['bitcoind', 'web']); var bitcoind = info.config.servicesConfig.bitcoind; - bitcoind.spawn.datadir.should.equal(home + '/.bitcoin'); - bitcoind.spawn.exec.should.equal(path.resolve(__dirname, '../../bin/bitcoind')); + bitcoind.connect.length.should.equal(1); }); it('be able to specify a network', function() { var info = defaultBaseConfig({network: 'testnet'}); info.config.network.should.equal('testnet'); }); - it('be able to specify a datadir', function() { - var info = defaultBaseConfig({datadir: './data2', network: 'testnet'}); - info.config.servicesConfig.bitcoind.spawn.datadir.should.equal('./data2'); - }); }); diff --git a/test/scaffold/default-config.integration.js b/test/scaffold/default-config.integration.js index 39bc1b26..ae694ff3 100644 --- a/test/scaffold/default-config.integration.js +++ b/test/scaffold/default-config.integration.js @@ -18,10 +18,15 @@ describe('#defaultConfig', function() { ], servicesConfig: { bitcoind: { - spawn: { - datadir: process.env.HOME + '/.btc/data', - exec: expectedExecPath - } + "connect": [{ + "zmqpubrawtx": "tcp://bitcoin-core:28332", + "zmqpubhashblock": "tcp://bitcoin-core:28332", + "rpcprotocol": "http", + "rpchost": "bitcoin-core", + "rpcport": 8332, + "rpcuser": "bitcoin", + "rpcpassword": "local321" + }] } } }, null, 2); @@ -48,8 +53,7 @@ describe('#defaultConfig', function() { info.config.services.should.deep.equal(['bitcoind', 'web']); var bitcoind = info.config.servicesConfig.bitcoind; should.exist(bitcoind); - bitcoind.spawn.datadir.should.equal(home + '/.btc/data'); - bitcoind.spawn.exec.should.equal(expectedExecPath); + bitcoind.connect.length.should.equal(1); }); it('will include additional services', function() { var config = JSON.stringify({ @@ -63,10 +67,15 @@ describe('#defaultConfig', function() { ], servicesConfig: { bitcoind: { - spawn: { - datadir: process.env.HOME + '/.btc/data', - exec: expectedExecPath - } + "connect": [{ + "zmqpubrawtx": "tcp://bitcoin-core:28332", + "zmqpubhashblock": "tcp://bitcoin-core:28332", + "rpcprotocol": "http", + "rpchost": "bitcoin-core", + "rpcport": 8332, + "rpcuser": "bitcoin", + "rpcpassword": "local321" + }] } } }, null, 2); @@ -100,7 +109,6 @@ describe('#defaultConfig', function() { ]); var bitcoind = info.config.servicesConfig.bitcoind; should.exist(bitcoind); - bitcoind.spawn.datadir.should.equal(home + '/.btc/data'); - bitcoind.spawn.exec.should.equal(expectedExecPath); + bitcoind.connect.length.should.equal(1); }); }); diff --git a/test/services/bitcoind.unit.js b/test/services/bitcoind.unit.js index 699c3da1..ba157b3c 100644 --- a/test/services/bitcoind.unit.js +++ b/test/services/bitcoind.unit.js @@ -341,179 +341,6 @@ describe('Bitcoin Service', function() { }); }); - describe('#_getDefaultConfig', function() { - it('will generate config file from defaults', function() { - var bitcoind = new BitcoinService(baseConfig); - var config = bitcoind._getDefaultConfig(); - config.should.equal(defaultBitcoinConf); - }); - }); - - describe('#_loadSpawnConfiguration', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will parse a bitcoin.conf file', function() { - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync, - existsSync: sinon.stub().returns(true), - writeFileSync: sinon.stub() - }, - mkdirp: { - sync: sinon.stub() - } - }); - var bitcoind = new TestBitcoin(baseConfig); - bitcoind.options.spawn.datadir = '/tmp/.bitcoin'; - var node = {}; - bitcoind._loadSpawnConfiguration(node); - should.exist(bitcoind.spawn.config); - bitcoind.spawn.config.should.deep.equal({ - addressindex: 1, - checkblocks: 144, - dbcache: 8192, - maxuploadtarget: 1024, - port: 20000, - rpcport: 50001, - rpcallowip: '127.0.0.1', - rpcuser: 'bitcoin', - rpcpassword: 'local321', - server: 1, - spentindex: 1, - timestampindex: 1, - txindex: 1, - upnp: 0, - whitelist: '127.0.0.1', - zmqpubhashblock: 'tcp://127.0.0.1:28332', - zmqpubrawtx: 'tcp://127.0.0.1:28332' - }); - }); - it('will expand relative datadir to absolute path', function() { - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync, - existsSync: sinon.stub().returns(true), - writeFileSync: sinon.stub() - }, - mkdirp: { - sync: sinon.stub() - } - }); - var config = { - node: { - network: btcLib.Networks.testnet, - configPath: '/tmp/.btc/btc-node.json' - }, - spawn: { - datadir: './data', - exec: 'testpath' - } - }; - var bitcoind = new TestBitcoin(config); - bitcoind.options.spawn.datadir = './data'; - var node = {}; - bitcoind._loadSpawnConfiguration(node); - bitcoind.options.spawn.datadir.should.equal('/tmp/.btc/data'); - }); - it('should throw an exception if txindex isn\'t enabled in the configuration', function() { - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/../data/badbitcoin.conf')), - existsSync: sinon.stub().returns(true), - }, - mkdirp: { - sync: sinon.stub() - } - }); - var bitcoind = new TestBitcoin(baseConfig); - (function() { - bitcoind._loadSpawnConfiguration({datadir: './test'}); - }).should.throw(btcLib.errors.InvalidState); - }); - it('should NOT set https options if node https options are set', function() { - var writeFileSync = function(path, config) { - config.should.equal(defaultBitcoinConf); - }; - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - writeFileSync: writeFileSync, - readFileSync: readFileSync, - existsSync: sinon.stub().returns(false) - }, - mkdirp: { - sync: sinon.stub() - } - }); - var config = { - node: { - network: { - name: 'regtest' - }, - https: true, - httpsOptions: { - key: 'key.pem', - cert: 'cert.pem' - } - }, - spawn: { - datadir: 'testdir', - exec: 'testexec' - } - }; - var bitcoind = new TestBitcoin(config); - bitcoind.options.spawn.datadir = '/tmp/.bitcoin'; - var node = {}; - bitcoind._loadSpawnConfiguration(node); - }); - }); - - describe('#_checkConfigIndexes', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'warn'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('should warn the user if reindex is set to 1 in the bitcoin.conf file', function() { - var bitcoind = new BitcoinService(baseConfig); - var config = { - txindex: 1, - addressindex: 1, - spentindex: 1, - server: 1, - zmqpubrawtx: 1, - zmqpubhashblock: 1, - reindex: 1 - }; - var node = {}; - bitcoind._checkConfigIndexes(config, node); - log.warn.callCount.should.equal(1); - node._reindex.should.equal(true); - }); - it('should warn if zmq port and hosts do not match', function() { - var bitcoind = new BitcoinService(baseConfig); - var config = { - txindex: 1, - addressindex: 1, - spentindex: 1, - server: 1, - zmqpubrawtx: 'tcp://127.0.0.1:28332', - zmqpubhashblock: 'tcp://127.0.0.1:28331', - reindex: 1 - }; - var node = {}; - (function() { - bitcoind._checkConfigIndexes(config, node); - }).should.throw('"zmqpubrawtx" and "zmqpubhashblock"'); - }); - }); - describe('#_resetCaches', function() { it('will reset LRU caches', function() { var bitcoind = new BitcoinService(baseConfig); @@ -1576,347 +1403,6 @@ describe('Bitcoin Service', function() { }); }); - describe('#_stopSpawnedProcess', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'warn'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('it will kill process and resume', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, '4321'); - var error = new Error('Test error'); - error.code = 'ENOENT'; - readFile.onCall(1).callsArgWith(2, error); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - bitcoind._process.kill = sinon.stub(); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - bitcoind._process.kill.callCount.should.equal(1); - log.warn.callCount.should.equal(1); - done(); - }); - }); - it('it will attempt to kill process and resume', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, '4321'); - var error = new Error('Test error'); - error.code = 'ENOENT'; - readFile.onCall(1).callsArgWith(2, error); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - var error2 = new Error('Test error'); - error2.code = 'ESRCH'; - bitcoind._process.kill = sinon.stub().throws(error2); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - bitcoind._process.kill.callCount.should.equal(1); - log.warn.callCount.should.equal(2); - done(); - }); - }); - it('it will attempt to kill process with NaN', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, ' '); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - bitcoind._process.kill = sinon.stub(); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - done(); - }); - }); - it('it will attempt to kill process without pid', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, ''); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - bitcoind._process.kill = sinon.stub(); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - done(); - }); - }); - }); - - describe('#_spawnChildProcess', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - sandbox.stub(log, 'warn'); - sandbox.stub(log, 'error'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will give error from spawn config', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind._loadSpawnConfiguration = sinon.stub().throws(new Error('test')); - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will exit spawn if shutdown', function() { - var config = { - node: { - network: btcLib.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(config); - bitcoind.spawn = {}; - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArgWith(0, null); - bitcoind.node.stopping = true; - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceOf(Error); - err.message.should.match(/Stopping while trying to spawn/); - }); - }); - it('will include network with spawn command and init zmq/rpc on node', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'testexec'; - bitcoind.spawn.configPath = 'testdir/bitcoin.conf'; - bitcoind.spawn.datadir = 'testdir'; - bitcoind.spawn.config = {}; - bitcoind.spawn.config.rpcport = 20001; - bitcoind.spawn.config.rpcuser = 'bitcoin'; - bitcoind.spawn.config.rpcpassword = 'password'; - bitcoind.spawn.config.zmqpubrawtx = 'tcp://127.0.0.1:30001'; - - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, null); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArgWith(1, null); - bitcoind._spawnChildProcess(function(err, node) { - should.not.exist(err); - bitcoind._loadTipFromNode.callCount.should.equal(1); - bitcoind._initZmqSubSocket.callCount.should.equal(1); - should.exist(bitcoind._initZmqSubSocket.args[0][0].client); - bitcoind._initZmqSubSocket.args[0][1].should.equal('tcp://127.0.0.1:30001'); - bitcoind._checkSyncedAndSubscribeZmqEvents.callCount.should.equal(1); - should.exist(bitcoind._checkSyncedAndSubscribeZmqEvents.args[0][0].client); - should.exist(node); - should.exist(node.client); - done(); - }); - }); - it('will respawn bitcoind spawned process', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'bitcoind'; - bitcoind.spawn.datadir = '/tmp/bitcoin'; - bitcoind.spawn.configPath = '/tmp/bitcoin/bitcoin.conf'; - bitcoind.spawn.config = {}; - bitcoind.spawnRestartTime = 1; - bitcoind._loadTipFromNode = sinon.stub().callsArg(1); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArg(1); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArg(0); - sinon.spy(bitcoind, '_spawnChildProcess'); - bitcoind._spawnChildProcess(function(err) { - if (err) { - return done(err); - } - process.once('exit', function() { - setTimeout(function() { - bitcoind._spawnChildProcess.callCount.should.equal(1); - done(); - }, 5); - }); - process.emit('exit', 1); - }); - }); - it('will NOT respawn bitcoind spawned process if shutting down', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var config = { - node: { - network: btcLib.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new TestBitcoinService(config); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'bitcoind'; - bitcoind.spawn.datadir = '/tmp/bitcoin'; - bitcoind.spawn.configPath = '/tmp/bitcoin/bitcoin.conf'; - bitcoind.spawn.config = {}; - bitcoind.spawnRestartTime = 1; - bitcoind._loadTipFromNode = sinon.stub().callsArg(1); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArg(1); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArg(0); - sinon.spy(bitcoind, '_spawnChildProcess'); - bitcoind._spawnChildProcess(function(err) { - if (err) { - return done(err); - } - bitcoind.node.stopping = true; - process.once('exit', function() { - setTimeout(function() { - bitcoind._spawnChildProcess.callCount.should.equal(1); - done(); - }, 5); - }); - process.emit('exit', 1); - }); - }); - it('will give error after 60 retries', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.startRetryInterval = 1; - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'testexec'; - bitcoind.spawn.configPath = 'testdir/bitcoin.conf'; - bitcoind.spawn.datadir = 'testdir'; - bitcoind.spawn.config = {}; - bitcoind.spawn.config.rpcport = 20001; - bitcoind.spawn.config.rpcuser = 'bitcoin'; - bitcoind.spawn.config.rpcpassword = 'password'; - bitcoind.spawn.config.zmqpubrawtx = 'tcp://127.0.0.1:30001'; - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind._spawnChildProcess(function(err) { - bitcoind._loadTipFromNode.callCount.should.equal(60); - err.should.be.instanceof(Error); - done(); - }); - }); - it('will give error from check reindex', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'testexec'; - bitcoind.spawn.configPath = 'testdir/bitcoin.conf'; - bitcoind.spawn.datadir = 'testdir'; - bitcoind.spawn.config = {}; - bitcoind.spawn.config.rpcport = 20001; - bitcoind.spawn.config.rpcuser = 'bitcoin'; - bitcoind.spawn.config.rpcpassword = 'password'; - bitcoind.spawn.config.zmqpubrawtx = 'tcp://127.0.0.1:30001'; - - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, null); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArgWith(1, new Error('test')); - - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceof(Error); - done(); - }); - }); - }); - describe('#_connectProcess', function() { it('will give error if connecting while shutting down', function(done) { var config = { @@ -1985,18 +1471,6 @@ describe('Bitcoin Service', function() { }); done(); }); - it('will give error from spawnChildProcess', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._spawnChildProcess = sinon.stub().callsArgWith(0, new Error('test')); - bitcoind.options = { - spawn: {} - }; - bitcoind.start(function(err) { - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); it('will give error from connectProcess', function(done) { var bitcoind = new BitcoinService(baseConfig); bitcoind._connectProcess = sinon.stub().callsArgWith(1, new Error('test')); @@ -2012,20 +1486,6 @@ describe('Bitcoin Service', function() { done(); }); }); - it('will push node from spawnChildProcess', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - bitcoind._initChain = sinon.stub().callsArg(0); - bitcoind._spawnChildProcess = sinon.stub().callsArgWith(0, null, node); - bitcoind.options = { - spawn: {} - }; - bitcoind.start(function(err) { - should.not.exist(err); - bitcoind.nodes.length.should.equal(1); - done(); - }); - }); it('will push node from connectProcess', function(done) { var bitcoind = new BitcoinService(baseConfig); bitcoind._initChain = sinon.stub().callsArg(0); @@ -4942,47 +4402,9 @@ describe('Bitcoin Service', function() { }); describe('#stop', function() { - it('will callback if spawn is not set', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.stop(done); - }); - it('will exit spawned process', function(done) { + it('will callback immediately', function(done) { var bitcoind = new BitcoinService(baseConfig); - bitcoind.spawn = {}; - bitcoind.spawn.process = new EventEmitter(); - bitcoind.spawn.process.kill = sinon.stub(); bitcoind.stop(done); - bitcoind.spawn.process.kill.callCount.should.equal(1); - bitcoind.spawn.process.kill.args[0][0].should.equal('SIGINT'); - bitcoind.spawn.process.emit('exit', 0); - }); - it('will give error with non-zero exit status code', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.spawn = {}; - bitcoind.spawn.process = new EventEmitter(); - bitcoind.spawn.process.kill = sinon.stub(); - bitcoind.stop(function(err) { - err.should.be.instanceof(Error); - err.code.should.equal(1); - done(); - }); - bitcoind.spawn.process.kill.callCount.should.equal(1); - bitcoind.spawn.process.kill.args[0][0].should.equal('SIGINT'); - bitcoind.spawn.process.emit('exit', 1); - }); - it('will stop after timeout', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.shutdownTimeout = 300; - bitcoind.spawn = {}; - bitcoind.spawn.process = new EventEmitter(); - bitcoind.spawn.process.kill = sinon.stub(); - bitcoind.stop(function(err) { - err.should.be.instanceof(Error); - done(); - }); - bitcoind.spawn.process.kill.callCount.should.equal(1); - bitcoind.spawn.process.kill.args[0][0].should.equal('SIGINT'); }); }); - });