diff --git a/lib/services/bitcoind.js b/lib/services/bitcoind.js index c459fd2d..60010eb0 100644 --- a/lib/services/bitcoind.js +++ b/lib/services/bitcoind.js @@ -847,9 +847,12 @@ Bitcoin.prototype._spawnChildProcess = function(callback) { } }); + var exitShutdown = false; + async.retry({times: 60, interval: self.startRetryInterval}, function(done) { if (self.node.stopping) { - return done(new Error('Stopping while trying to connect to bitcoind.')); + exitShutdown = true; + return done(); } node.client = new BitcoinRPC({ @@ -866,6 +869,9 @@ Bitcoin.prototype._spawnChildProcess = function(callback) { if (err) { return callback(err); } + if (exitShutdown) { + return callback(new Error('Stopping while trying to spawn bitcoind.')); + } self._initZmqSubSocket(node, self.spawn.config.zmqpubrawtx); diff --git a/test/services/bitcoind.unit.js b/test/services/bitcoind.unit.js index d6b8f0cf..bd312b2a 100644 --- a/test/services/bitcoind.unit.js +++ b/test/services/bitcoind.unit.js @@ -1569,12 +1569,14 @@ describe('Bitcoin Service', function() { 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); @@ -1582,6 +1584,45 @@ describe('Bitcoin Service', function() { done(); }); }); + it('will give error from stopSpawnedBitcoin', function() { + var bitcoind = new BitcoinService(baseConfig); + bitcoind._loadSpawnConfiguration = sinon.stub(); + bitcoind._stopSpawnedBitcoin = sinon.stub().callsArgWith(0, new Error('test')); + bitcoind._spawnChildProcess(function(err) { + err.should.be.instanceOf(Error); + err.message.should.equal('test'); + }); + }); + it('will exit spawn if shutdown', function() { + var config = { + node: { + network: bitcore.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); @@ -1671,6 +1712,92 @@ describe('Bitcoin Service', function() { process.emit('exit', 1); }); }); + it('will emit error during respawn', 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); + } + bitcoind._spawnChildProcess = sinon.stub().callsArgWith(0, new Error('test')); + bitcoind.on('error', function(err) { + err.should.be.instanceOf(Error); + err.message.should.equal('test'); + done(); + }); + 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: bitcore.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);