bitcore-node-zcash/lib/node.js

288 lines
7.3 KiB
JavaScript
Raw Normal View History

'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var async = require('async');
2015-10-16 21:56:29 -07:00
var bitcore = require('bitcore-lib');
var Networks = bitcore.Networks;
2015-07-21 06:09:59 -07:00
var $ = bitcore.util.preconditions;
2015-08-31 10:38:21 -07:00
var _ = bitcore.deps._;
var index = require('./');
var log = index.log;
2015-07-29 10:36:23 -07:00
var Bus = require('./bus');
2015-08-28 10:54:29 -07:00
var errors = require('./errors');
2015-09-22 08:38:14 -07:00
/**
* A node is a hub of services, and will manage starting and stopping the services in
* the correct order based the the dependency chain. The node also holds common configuration
* properties that can be shared across services, such as network settings.
*
* The array of services should have the format:
* ```js
2015-09-22 08:38:14 -07:00
* {
* name: 'bitcoind',
* config: {}, // options to pass into constructor
* module: ServiceConstructor
2015-09-22 08:38:14 -07:00
* }
* ```
2015-09-22 08:38:14 -07:00
*
* @param {Object} config - The configuration of the node
* @param {Array} config.services - The array of services
* @param {Number} config.port - The HTTP port for services
* @param {Boolean} config.https - Enable https
* @param {Object} config.httpsOptions - Options for https
* @param {String} config.httpsOptions.key - Path to key file
* @param {String} config.httpsOptions.cert - Path to cert file
* @param {}
*/
function Node(config) {
2015-09-22 08:38:14 -07:00
/* jshint maxstatements: 20 */
if(!(this instanceof Node)) {
return new Node(config);
}
2016-04-07 07:14:34 -07:00
this.configPath = config.path;
2015-09-22 08:38:14 -07:00
this.errors = errors;
2015-08-31 10:38:21 -07:00
this.log = log;
2015-08-31 06:00:00 -07:00
this.network = null;
2015-08-31 06:00:00 -07:00
this.services = {};
this._unloadedServices = [];
2015-08-27 13:09:27 -07:00
2015-08-31 06:00:00 -07:00
// TODO type check the arguments of config.services
if (config.services) {
$.checkArgument(Array.isArray(config.services));
this._unloadedServices = config.services;
2015-08-27 13:09:27 -07:00
}
2015-08-31 10:05:46 -07:00
this.port = config.port;
2015-09-10 08:06:37 -07:00
this.https = config.https;
this.httpsOptions = config.httpsOptions;
2015-08-31 06:00:00 -07:00
this._setNetwork(config);
}
2015-08-31 06:00:00 -07:00
util.inherits(Node, EventEmitter);
2015-09-22 08:38:14 -07:00
/**
* Will set the this.network based on a network string.
* @param {Object} config
* @param {String} config.network - Possible options "testnet", "regtest" or "livenet"
*/
2015-08-31 06:00:00 -07:00
Node.prototype._setNetwork = function(config) {
if (config.network === 'testnet') {
this.network = Networks.get('testnet');
} else if (config.network === 'regtest') {
2016-02-03 15:29:33 -08:00
Networks.enableRegtest();
this.network = Networks.get('regtest');
} else {
2015-08-31 06:00:00 -07:00
this.network = Networks.defaultNetwork;
}
2015-07-21 06:09:59 -07:00
$.checkState(this.network, 'Unrecognized network');
};
2015-09-22 08:38:14 -07:00
/**
* Will instantiate a new Bus for this node.
* @returns {Bus}
*/
2015-08-31 06:00:00 -07:00
Node.prototype.openBus = function() {
2015-08-31 06:00:00 -07:00
return new Bus({node: this});
2015-08-24 11:46:48 -07:00
};
2015-09-22 08:38:14 -07:00
/**
* Will get an array of API method descriptions from all of the available services.
* @returns {Array}
*/
2015-08-31 06:00:00 -07:00
Node.prototype.getAllAPIMethods = function() {
var methods = [];
2015-08-31 06:00:00 -07:00
for(var i in this.services) {
var mod = this.services[i];
2015-08-31 06:00:00 -07:00
methods = methods.concat(mod.getAPIMethods());
}
return methods;
};
2015-09-22 08:38:14 -07:00
/**
* Will get an array of events from all of the available services.
* @returns {Array}
*/
2015-08-31 06:00:00 -07:00
Node.prototype.getAllPublishEvents = function() {
var events = [];
2015-08-31 06:00:00 -07:00
for (var i in this.services) {
var mod = this.services[i];
2015-08-31 06:00:00 -07:00
events = events.concat(mod.getPublishEvents());
}
return events;
};
2015-09-22 08:38:14 -07:00
/**
* Will organize services into the order that they should be started
* based on the service's dependencies.
* @returns {Array}
*/
2015-08-27 13:09:27 -07:00
Node.prototype.getServiceOrder = function() {
2015-08-31 06:00:00 -07:00
var services = this._unloadedServices;
2015-08-27 13:09:27 -07:00
// organize data for sorting
var names = [];
var servicesByName = {};
for (var i = 0; i < services.length; i++) {
var service = services[i];
names.push(service.name);
servicesByName[service.name] = service;
2015-08-20 14:50:14 -07:00
}
2015-08-27 13:09:27 -07:00
var stackNames = {};
var stack = [];
function addToStack(names) {
for(var i = 0; i < names.length; i++) {
var name = names[i];
var service = servicesByName[name];
2015-08-31 06:00:00 -07:00
$.checkState(service, 'Required dependency "' + name + '" not available.');
2015-08-27 13:09:27 -07:00
// first add the dependencies
2015-08-31 10:38:21 -07:00
addToStack(service.module.dependencies);
2015-08-27 13:09:27 -07:00
// add to the stack if it hasn't been added
if(!stackNames[name]) {
stack.push(service);
stackNames[name] = true;
}
2015-08-20 14:50:14 -07:00
}
}
2015-08-27 13:09:27 -07:00
addToStack(names);
2015-08-20 14:50:14 -07:00
return stack;
};
2015-09-22 08:38:14 -07:00
/**
* Will instantiate an instance of the service module, add it to the node
* services, start the service and add available API methods to the node and
* checking for any conflicts.
* @param {Object} serviceInfo
* @param {String} serviceInfo.name - The name of the service
* @param {Object} serviceInfo.module - The service module constructor
* @param {Object} serviceInfo.config - Options to pass into the constructor
* @param {Function} callback - Called when the service is started
* @private
*/
Node.prototype._startService = function(serviceInfo, callback) {
2015-08-31 06:00:00 -07:00
var self = this;
2015-08-31 10:37:11 -07:00
$.checkState(_.isObject(serviceInfo.config));
$.checkState(!serviceInfo.config.node);
$.checkState(!serviceInfo.config.name);
2015-08-31 10:38:21 -07:00
log.info('Starting ' + serviceInfo.name);
var config = serviceInfo.config;
2015-08-31 10:37:11 -07:00
config.node = this;
config.name = serviceInfo.name;
var service = new serviceInfo.module(config);
// include in loaded services
self.services[serviceInfo.name] = service;
service.start(function(err) {
if (err) {
return callback(err);
}
// add API methods
var methodData = service.getAPIMethods();
var methodNameConflicts = [];
methodData.forEach(function(data) {
var name = data[0];
var instance = data[1];
var method = data[2];
if (self[name]) {
methodNameConflicts.push(name);
} else {
self[name] = function() {
return method.apply(instance, arguments);
};
}
});
if (methodNameConflicts.length > 0) {
return callback(new Error('Existing API method(s) exists: ' + methodNameConflicts.join(', ')));
2015-08-31 06:00:00 -07:00
}
callback();
2015-08-31 06:00:00 -07:00
});
2015-08-31 06:00:00 -07:00
};
2016-04-07 07:14:34 -07:00
Node.prototype._logTitle = function() {
2016-04-08 11:44:24 -07:00
if (this.configPath) {
log.info('Using config:', this.configPath);
}
2016-04-07 07:14:34 -07:00
};
2015-09-22 08:38:14 -07:00
/**
* Will start all running services in the order based on the dependency chain.
* @param {Function} callback - Called when all services are started
*/
2015-08-20 14:50:14 -07:00
Node.prototype.start = function(callback) {
var self = this;
var servicesOrder = this.getServiceOrder();
2015-08-20 14:50:14 -07:00
2016-04-07 07:14:34 -07:00
self._logTitle();
2015-08-20 14:50:14 -07:00
async.eachSeries(
servicesOrder,
2015-08-20 14:50:14 -07:00
function(service, next) {
self._startService(service, next);
},
function(err) {
if (err) {
2015-08-31 06:00:00 -07:00
return callback(err);
2015-08-27 13:09:27 -07:00
}
self.emit('ready');
callback();
}
2015-08-20 14:50:14 -07:00
);
};
2016-04-08 13:00:33 -07:00
Node.prototype.getNetworkName = function() {
var network = this.network.name;
if (this.network.regtestEnabled) {
network = 'regtest';
}
return network;
};
2015-09-22 08:38:14 -07:00
/**
* Will stop all running services in the reverse order that they
* were initially started.
* @param {Function} callback - Called when all services are stopped
*/
2015-08-20 14:50:14 -07:00
Node.prototype.stop = function(callback) {
2015-08-21 12:39:46 -07:00
log.info('Beginning shutdown');
2015-08-20 14:50:14 -07:00
var self = this;
var services = this.getServiceOrder().reverse();
2015-08-21 08:53:20 -07:00
this.stopping = true;
this.emit('stopping');
2015-08-20 14:50:14 -07:00
async.eachSeries(
services,
function(service, next) {
if (self.services[service.name]) {
log.info('Stopping ' + service.name);
self.services[service.name].stop(next);
} else {
log.info('Stopping ' + service.name + ' (not started)');
setImmediate(next);
}
2015-08-20 14:50:14 -07:00
},
callback
);
};
module.exports = Node;