5 changed files with 605 additions and 13 deletions
@ -0,0 +1,201 @@ |
|||
'use strict'; |
|||
|
|||
var util = require('util'); |
|||
var fs = require('fs'); |
|||
var io = require('socket.io'); |
|||
var https = require('https'); |
|||
var http = require('http'); |
|||
var async = require('async'); |
|||
var path = require('path'); |
|||
var bitcore = require('bitcore'); |
|||
var Networks = bitcore.Networks; |
|||
var Locker = require('locker-server'); |
|||
var BlockchainMonitor = require('../lib/blockchainmonitor'); |
|||
var EmailService = require('../lib/emailservice'); |
|||
var ExpressApp = require('../lib/expressapp'); |
|||
var WsApp = require('../lib/wsapp'); |
|||
var child_process = require('child_process'); |
|||
var spawn = child_process.spawn; |
|||
var EventEmitter = require('events').EventEmitter; |
|||
var baseConfig = require('../config'); |
|||
|
|||
/** |
|||
* A Bitcore Node Service module |
|||
* @param {Object} options |
|||
* @param {Node} options.node - A reference to the Bitcore Node instance |
|||
-* @param {Boolean} options.https - Enable https for this module, defaults to node settings. |
|||
* @param {Number} options.bwsPort - Port for Bitcore Wallet Service API |
|||
* @param {Number} options.messageBrokerPort - Port for BWS message broker |
|||
* @param {Number} options.lockerPort - Port for BWS locker port |
|||
*/ |
|||
var Service = function(options) { |
|||
EventEmitter.call(this); |
|||
|
|||
this.node = options.node; |
|||
this.https = options.https || this.node.https; |
|||
this.httpsOptions = options.httpsOptions || this.node.httpsOptions; |
|||
this.bwsPort = options.bwsPort || baseConfig.port; |
|||
this.messageBrokerPort = options.messageBrokerPort || 3380; |
|||
if (baseConfig.lockOpts) { |
|||
this.lockerPort = baseConfig.lockOpts.lockerServer.port; |
|||
} |
|||
this.lockerPort = options.lockerPort || this.lockerPort; |
|||
}; |
|||
|
|||
util.inherits(Service, EventEmitter); |
|||
|
|||
Service.dependencies = ['insight-api']; |
|||
|
|||
/** |
|||
* This method will read `key` and `cert` files from disk based on `httpsOptions` and |
|||
* return `serverOpts` with the read files. |
|||
* @returns {Object} |
|||
*/ |
|||
Service.prototype._readHttpsOptions = function() { |
|||
if(!this.httpsOptions || !this.httpsOptions.key || !this.httpsOptions.cert) { |
|||
throw new Error('Missing https options'); |
|||
} |
|||
|
|||
var serverOpts = {}; |
|||
serverOpts.key = fs.readFileSync(this.httpsOptions.key); |
|||
serverOpts.cert = fs.readFileSync(this.httpsOptions.cert); |
|||
|
|||
// This sets the intermediate CA certs only if they have all been designated in the config.js
|
|||
if (this.httpsOptions.CAinter1 && this.httpsOptions.CAinter2 && this.httpsOptions.CAroot) { |
|||
serverOpts.ca = [ |
|||
fs.readFileSync(this.httpsOptions.CAinter1), |
|||
fs.readFileSync(this.httpsOptions.CAinter2), |
|||
fs.readFileSync(this.httpsOptions.CAroot) |
|||
]; |
|||
} |
|||
return serverOpts; |
|||
}; |
|||
|
|||
/** |
|||
* Will get the configuration with settings for the locally |
|||
* running Insight API. |
|||
* @returns {Object} |
|||
*/ |
|||
Service.prototype._getConfiguration = function() { |
|||
var self = this; |
|||
|
|||
var providerOptions = { |
|||
provider: 'insight', |
|||
url: 'http://localhost:' + self.node.port, |
|||
apiPrefix: '/insight-api' |
|||
}; |
|||
|
|||
// A bitcore-node is either livenet or testnet, so we'll pass
|
|||
// the configuration options to communicate via the local running
|
|||
// instance of the insight-api service.
|
|||
if (self.node.network === Networks.livenet) { |
|||
baseConfig.blockchainExplorerOpts = { |
|||
livenet: providerOptions |
|||
}; |
|||
} else if (self.node.network === Networks.testnet) { |
|||
baseConfig.blockchainExplorerOpts = { |
|||
testnet: providerOptions |
|||
}; |
|||
} else { |
|||
throw new Error('Unknown network'); |
|||
} |
|||
|
|||
return baseConfig; |
|||
|
|||
}; |
|||
|
|||
/** |
|||
* Will start the HTTP web server and socket.io for the wallet service. |
|||
*/ |
|||
Service.prototype._startWalletService = function(config, next) { |
|||
var self = this; |
|||
var expressApp = new ExpressApp(); |
|||
var wsApp = new WsApp(); |
|||
|
|||
if (self.https) { |
|||
var serverOpts = self._readHttpsOptions(); |
|||
self.server = https.createServer(serverOpts, expressApp.app); |
|||
} else { |
|||
self.server = http.Server(expressApp.app); |
|||
} |
|||
|
|||
async.parallel([ |
|||
function(done) { |
|||
expressApp.start(config, done); |
|||
}, |
|||
function(done) { |
|||
wsApp.start(self.server, config, done); |
|||
}, |
|||
], function(err) { |
|||
if (err) { |
|||
return next(err); |
|||
} |
|||
self.server.listen(self.bwsPort, next); |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* Called by the node to start the service |
|||
*/ |
|||
Service.prototype.start = function(done) { |
|||
|
|||
var self = this; |
|||
var config; |
|||
try { |
|||
config = self._getConfiguration(); |
|||
} catch(err) { |
|||
return done(err); |
|||
} |
|||
|
|||
// Locker Server
|
|||
var locker = new Locker(); |
|||
locker.listen(self.lockerPort); |
|||
|
|||
// Message Broker
|
|||
var messageServer = io(self.messageBrokerPort); |
|||
messageServer.on('connection', function(s) { |
|||
s.on('msg', function(d) { |
|||
messageServer.emit('msg', d); |
|||
}); |
|||
}); |
|||
|
|||
async.series([ |
|||
function(next) { |
|||
// Blockchain Monitor
|
|||
var blockChainMonitor = new BlockchainMonitor(); |
|||
blockChainMonitor.start(config, next); |
|||
}, |
|||
function(next) { |
|||
// Email Service
|
|||
if (config.emailOpts) { |
|||
var emailService = new EmailService(); |
|||
emailService.start(config, next); |
|||
} else { |
|||
setImmediate(next); |
|||
} |
|||
}, |
|||
function(next) { |
|||
self._startWalletService(config, next); |
|||
} |
|||
], done); |
|||
|
|||
}; |
|||
|
|||
/** |
|||
* Called by node to stop the service |
|||
*/ |
|||
Service.prototype.stop = function(done) { |
|||
setImmediate(function() { |
|||
done(); |
|||
}); |
|||
}; |
|||
|
|||
Service.prototype.getAPIMethods = function() { |
|||
return []; |
|||
}; |
|||
|
|||
Service.prototype.getPublishEvents = function() { |
|||
return []; |
|||
}; |
|||
|
|||
module.exports = Service; |
@ -0,0 +1,380 @@ |
|||
'use strict'; |
|||
|
|||
var should = require('chai').should(); |
|||
var proxyquire = require('proxyquire'); |
|||
var bitcore = require('bitcore'); |
|||
var sinon = require('sinon'); |
|||
var Service = require('../bitcorenode'); |
|||
|
|||
describe('Bitcore Node Service', function() { |
|||
describe('#constructor', function() { |
|||
it('https settings from node', function() { |
|||
var node = { |
|||
https: true, |
|||
httpsOptions: { |
|||
key: 'key', |
|||
cert: 'cert' |
|||
} |
|||
}; |
|||
var options = { |
|||
node: node |
|||
}; |
|||
var service = new Service(options); |
|||
service.node.should.equal(node); |
|||
service.https.should.equal(true); |
|||
service.httpsOptions.should.deep.equal({ |
|||
key: 'key', |
|||
cert: 'cert' |
|||
}); |
|||
service.bwsPort.should.equal(3232); |
|||
service.messageBrokerPort.should.equal(3380); |
|||
service.lockerPort.should.equal(3231); |
|||
}); |
|||
it('direct https options', function() { |
|||
var node = {}; |
|||
var options = { |
|||
node: node, |
|||
https: true, |
|||
httpsOptions: { |
|||
key: 'key', |
|||
cert: 'cert' |
|||
} |
|||
}; |
|||
var service = new Service(options); |
|||
service.https.should.equal(true); |
|||
service.httpsOptions.should.deep.equal({ |
|||
key: 'key', |
|||
cert: 'cert' |
|||
}); |
|||
service.bwsPort.should.equal(3232); |
|||
service.messageBrokerPort.should.equal(3380); |
|||
service.lockerPort.should.equal(3231); |
|||
}); |
|||
it('can set custom ports', function() { |
|||
var node = {}; |
|||
var options = { |
|||
node: node, |
|||
bwsPort: 1000, |
|||
messageBrokerPort: 1001, |
|||
lockerPort: 1002 |
|||
}; |
|||
var service = new Service(options); |
|||
service.bwsPort.should.equal(1000); |
|||
service.messageBrokerPort.should.equal(1001); |
|||
service.lockerPort.should.equal(1002); |
|||
}); |
|||
}); |
|||
describe('#readHttpsOptions', function() { |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
fs: { |
|||
readFileSync: function(arg) { |
|||
return arg; |
|||
} |
|||
} |
|||
}); |
|||
it('will create server options from httpsOptions', function() { |
|||
var options = { |
|||
node: { |
|||
https: true, |
|||
httpsOptions: { |
|||
key: 'key', |
|||
cert: 'cert', |
|||
CAinter1: 'CAinter1', |
|||
CAinter2: 'CAinter2', |
|||
CAroot: 'CAroot' |
|||
} |
|||
} |
|||
}; |
|||
var service = new TestService(options); |
|||
var serverOptions = service._readHttpsOptions(); |
|||
serverOptions.key.should.equal('key'); |
|||
serverOptions.cert.should.equal('cert'); |
|||
serverOptions.ca[0].should.equal('CAinter1'); |
|||
serverOptions.ca[1].should.equal('CAinter2'); |
|||
serverOptions.ca[2].should.equal('CAroot'); |
|||
}); |
|||
}); |
|||
describe('#_getConfiguration', function() { |
|||
it('will throw with an unknown network', function() { |
|||
var options = { |
|||
node: { |
|||
network: 'unknown' |
|||
} |
|||
}; |
|||
var service = new Service(options); |
|||
(function() { |
|||
service._getConfiguration(); |
|||
}).should.throw('Unknown network'); |
|||
}); |
|||
it('livenet local insight', function() { |
|||
var options = { |
|||
node: { |
|||
network: bitcore.Networks.livenet, |
|||
port: 3001 |
|||
} |
|||
}; |
|||
var service = new Service(options); |
|||
var config = service._getConfiguration(); |
|||
config.blockchainExplorerOpts.livenet.should.deep.equal({ |
|||
'apiPrefix': '/insight-api', |
|||
'provider': 'insight', |
|||
'url': 'http://localhost:3001' |
|||
}); |
|||
}); |
|||
it('testnet local insight', function() { |
|||
var options = { |
|||
node: { |
|||
network: bitcore.Networks.testnet, |
|||
port: 3001 |
|||
} |
|||
}; |
|||
var service = new Service(options); |
|||
var config = service._getConfiguration(); |
|||
config.blockchainExplorerOpts.testnet.should.deep.equal({ |
|||
'apiPrefix': '/insight-api', |
|||
'provider': 'insight', |
|||
'url': 'http://localhost:3001' |
|||
}); |
|||
}); |
|||
}); |
|||
describe('#_startWalletService', function() { |
|||
it('will start express and web socket servers', function(done) { |
|||
function TestExpressApp() {} |
|||
TestExpressApp.prototype.start = sinon.stub().callsArg(1); |
|||
function TestWSApp() {} |
|||
TestWSApp.prototype.start = sinon.stub().callsArg(2); |
|||
var listen = sinon.stub().callsArg(1); |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/expressapp': TestExpressApp, |
|||
'../lib/wsapp': TestWSApp, |
|||
'http': { |
|||
Server: sinon.stub().returns({ |
|||
listen: listen |
|||
}) |
|||
} |
|||
}); |
|||
var options = { |
|||
node: { |
|||
bwsPort: 3232 |
|||
} |
|||
}; |
|||
var service = new TestService(options); |
|||
var config = {}; |
|||
service._startWalletService(config, function(err) { |
|||
if (err) { |
|||
throw err; |
|||
} |
|||
TestExpressApp.prototype.start.callCount.should.equal(1); |
|||
TestExpressApp.prototype.start.args[0][0].should.equal(config); |
|||
TestExpressApp.prototype.start.args[0][1].should.be.a('function'); |
|||
TestWSApp.prototype.start.callCount.should.equal(1); |
|||
TestWSApp.prototype.start.args[0][0].should.equal(service.server); |
|||
TestWSApp.prototype.start.args[0][1].should.equal(config); |
|||
TestWSApp.prototype.start.args[0][2].should.be.a('function'); |
|||
listen.callCount.should.equal(1); |
|||
listen.args[0][0].should.equal(3232); |
|||
listen.args[0][1].should.be.a('function'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('error from express', function(done) { |
|||
function TestExpressApp() {} |
|||
TestExpressApp.prototype.start = sinon.stub().callsArgWith(1, new Error('test')); |
|||
function TestWSApp() {} |
|||
TestWSApp.prototype.start = sinon.stub().callsArg(2); |
|||
var listen = sinon.stub().callsArg(1); |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/expressapp': TestExpressApp, |
|||
'../lib/wsapp': TestWSApp, |
|||
'http': { |
|||
Server: sinon.stub().returns({ |
|||
listen: listen |
|||
}) |
|||
} |
|||
}); |
|||
var options = { |
|||
node: { |
|||
bwsPort: 3232 |
|||
} |
|||
}; |
|||
var service = new TestService(options); |
|||
var config = {}; |
|||
service._startWalletService(config, function(err) { |
|||
err.message.should.equal('test'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('error from web socket', function(done) { |
|||
function TestExpressApp() {} |
|||
TestExpressApp.prototype.start = sinon.stub().callsArg(1); |
|||
function TestWSApp() {} |
|||
TestWSApp.prototype.start = sinon.stub().callsArgWith(2, new Error('test')); |
|||
var listen = sinon.stub().callsArg(1); |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/expressapp': TestExpressApp, |
|||
'../lib/wsapp': TestWSApp, |
|||
'http': { |
|||
Server: sinon.stub().returns({ |
|||
listen: listen |
|||
}) |
|||
} |
|||
}); |
|||
var options = { |
|||
node: { |
|||
bwsPort: 3232 |
|||
} |
|||
}; |
|||
var service = new TestService(options); |
|||
var config = {}; |
|||
service._startWalletService(config, function(err) { |
|||
err.message.should.equal('test'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('error from server.listen', function(done) { |
|||
var app = {}; |
|||
function TestExpressApp() { |
|||
this.app = app; |
|||
} |
|||
TestExpressApp.prototype.start = sinon.stub().callsArg(1); |
|||
function TestWSApp() {} |
|||
TestWSApp.prototype.start = sinon.stub().callsArg(2); |
|||
var listen = sinon.stub().callsArgWith(1, new Error('test')); |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/expressapp': TestExpressApp, |
|||
'../lib/wsapp': TestWSApp, |
|||
'http': { |
|||
Server: function() { |
|||
arguments[0].should.equal(app); |
|||
return { |
|||
listen: listen |
|||
}; |
|||
} |
|||
} |
|||
}); |
|||
var options = { |
|||
node: { |
|||
bwsPort: 3232 |
|||
} |
|||
}; |
|||
var service = new TestService(options); |
|||
var config = {}; |
|||
service._startWalletService(config, function(err) { |
|||
err.message.should.equal('test'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('will enable https', function(done) { |
|||
var app = {}; |
|||
function TestExpressApp() { |
|||
this.app = app; |
|||
} |
|||
TestExpressApp.prototype.start = sinon.stub().callsArg(1); |
|||
function TestWSApp() {} |
|||
TestWSApp.prototype.start = sinon.stub().callsArg(2); |
|||
var listen = sinon.stub().callsArg(1); |
|||
var httpsOptions = {}; |
|||
var createServer = function() { |
|||
arguments[0].should.equal(httpsOptions); |
|||
arguments[1].should.equal(app); |
|||
return { |
|||
listen: listen |
|||
}; |
|||
}; |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/expressapp': TestExpressApp, |
|||
'../lib/wsapp': TestWSApp, |
|||
'https': { |
|||
createServer: createServer |
|||
} |
|||
}); |
|||
var options = { |
|||
node: { |
|||
https: true, |
|||
bwsPort: 3232 |
|||
} |
|||
}; |
|||
var service = new TestService(options); |
|||
service._readHttpsOptions = sinon.stub().returns(httpsOptions); |
|||
var config = {}; |
|||
service._startWalletService(config, function(err) { |
|||
service._readHttpsOptions.callCount.should.equal(1); |
|||
listen.callCount.should.equal(1); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
describe('#start', function(done) { |
|||
it('error from configuration', function(done) { |
|||
var options = { |
|||
node: {} |
|||
}; |
|||
var service = new Service(options); |
|||
service._getConfiguration = function() { |
|||
throw new Error('test'); |
|||
}; |
|||
service.start(function(err) { |
|||
err.message.should.equal('test'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('error from blockchain monitor', function(done) { |
|||
var app = {}; |
|||
function TestBlockchainMonitor() {} |
|||
TestBlockchainMonitor.prototype.start = sinon.stub().callsArgWith(1, new Error('test')); |
|||
function TestLocker() {} |
|||
TestLocker.prototype.listen = sinon.stub(); |
|||
function TestEmailService() {} |
|||
TestEmailService.prototype.start = sinon.stub(); |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/blockchainmonitor': TestBlockchainMonitor, |
|||
'../lib/emailservice': TestEmailService, |
|||
'socket.io': sinon.stub().returns({ |
|||
on: sinon.stub() |
|||
}), |
|||
'locker-server': TestLocker, |
|||
}); |
|||
var options = { |
|||
node: {} |
|||
}; |
|||
var service = new TestService(options); |
|||
var config = {}; |
|||
service._getConfiguration = sinon.stub().returns(config); |
|||
service._startWalletService = sinon.stub().callsArg(1); |
|||
service.start(function(err) { |
|||
err.message.should.equal('test'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('error from email service', function(done) { |
|||
var app = {}; |
|||
function TestBlockchainMonitor() {} |
|||
TestBlockchainMonitor.prototype.start = sinon.stub().callsArg(1); |
|||
function TestLocker() {} |
|||
TestLocker.prototype.listen = sinon.stub(); |
|||
function TestEmailService() {} |
|||
TestEmailService.prototype.start = sinon.stub().callsArgWith(1, new Error('test')); |
|||
var TestService = proxyquire('../bitcorenode', { |
|||
'../lib/blockchainmonitor': TestBlockchainMonitor, |
|||
'../lib/emailservice': TestEmailService, |
|||
'socket.io': sinon.stub().returns({ |
|||
on: sinon.stub() |
|||
}), |
|||
'locker-server': TestLocker, |
|||
}); |
|||
var options = { |
|||
node: {} |
|||
}; |
|||
var service = new TestService(options); |
|||
service._getConfiguration = sinon.stub().returns({ |
|||
emailOpts: {} |
|||
}); |
|||
var config = {}; |
|||
service._startWalletService = sinon.stub().callsArg(1); |
|||
service.start(function(err) { |
|||
err.message.should.equal('test'); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
Loading…
Reference in new issue