2014-02-20 15:13:50 -08:00
|
|
|
var fs = require('fs');
|
2014-02-28 19:12:59 -08:00
|
|
|
var os = require('os');
|
|
|
|
var cluster = require('cluster');
|
|
|
|
|
|
|
|
|
2014-03-22 23:46:59 -07:00
|
|
|
var async = require('async');
|
2014-03-14 12:02:55 -07:00
|
|
|
var posix = require('posix');
|
2014-03-14 14:56:54 -07:00
|
|
|
var PoolLogger = require('./libs/logUtil.js');
|
2014-03-14 12:02:55 -07:00
|
|
|
var BlocknotifyListener = require('./libs/blocknotifyListener.js');
|
|
|
|
var RedisBlocknotifyListener = require('./libs/redisblocknotifyListener.js');
|
|
|
|
var WorkerListener = require('./libs/workerListener.js');
|
|
|
|
var PoolWorker = require('./libs/poolWorker.js');
|
|
|
|
var PaymentProcessor = require('./libs/paymentProcessor.js');
|
2014-03-14 14:56:54 -07:00
|
|
|
var Website = require('./libs/website.js');
|
2014-01-16 11:18:16 -08:00
|
|
|
JSON.minify = JSON.minify || require("node-json-minify");
|
|
|
|
|
2014-03-13 14:03:28 -07:00
|
|
|
var portalConfig = JSON.parse(JSON.minify(fs.readFileSync("config.json", {encoding: 'utf8'})));
|
2014-03-20 15:25:59 -07:00
|
|
|
|
2014-02-28 19:12:59 -08:00
|
|
|
|
2014-03-22 23:16:06 -07:00
|
|
|
var logger = new PoolLogger({
|
2014-03-13 14:03:28 -07:00
|
|
|
logLevel: portalConfig.logLevel
|
2014-01-14 08:56:06 -08:00
|
|
|
});
|
2014-01-13 17:32:54 -08:00
|
|
|
|
2014-03-22 23:16:06 -07:00
|
|
|
|
2014-01-13 17:32:54 -08:00
|
|
|
|
|
|
|
|
2014-03-20 15:25:59 -07:00
|
|
|
try {
|
|
|
|
require('newrelic');
|
|
|
|
if (cluster.isMaster)
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.debug('NewRelic', 'Monitor', 'New Relic initiated');
|
2014-03-20 15:25:59 -07:00
|
|
|
} catch(e) {}
|
|
|
|
|
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
//Try to give process ability to handle 100k concurrent connections
|
|
|
|
try{
|
|
|
|
posix.setrlimit('nofile', { soft: 100000, hard: 100000 });
|
|
|
|
}
|
|
|
|
catch(e){
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.warning('POSIX', 'Connection Limit', '(Safe to ignore) Must be ran as root to increase resource limits');
|
2014-03-09 19:31:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (cluster.isWorker){
|
2014-03-14 12:02:55 -07:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
switch(process.env.workerType){
|
|
|
|
case 'pool':
|
2014-03-22 23:16:06 -07:00
|
|
|
new PoolWorker(logger);
|
2014-03-09 19:31:58 -07:00
|
|
|
break;
|
|
|
|
case 'paymentProcessor':
|
2014-03-22 23:16:06 -07:00
|
|
|
new PaymentProcessor(logger);
|
2014-03-09 19:31:58 -07:00
|
|
|
break;
|
2014-03-12 23:37:27 -07:00
|
|
|
case 'website':
|
2014-03-22 23:16:06 -07:00
|
|
|
new Website(logger);
|
2014-03-12 23:37:27 -07:00
|
|
|
break;
|
2014-03-09 19:31:58 -07:00
|
|
|
}
|
2014-01-13 17:32:54 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
return;
|
2014-03-14 14:58:21 -07:00
|
|
|
} /* else {
|
2014-03-14 12:02:55 -07:00
|
|
|
var coinNames = ['alphacoin','frankocoin','emerald','kittehcoin'];
|
|
|
|
var curIndex = 0;
|
|
|
|
setInterval(function () {
|
|
|
|
var newCoinName = coinNames[++curIndex % coinNames.length];
|
|
|
|
console.log("SWITCHING to "+newCoinName);
|
|
|
|
var ipcMessage = {type:'switch', coin: newCoinName};
|
|
|
|
Object.keys(cluster.workers).forEach(function(id) {
|
|
|
|
cluster.workers[id].send(ipcMessage);
|
|
|
|
});
|
2014-03-14 14:56:54 -07:00
|
|
|
}, 20000);
|
2014-03-14 14:58:21 -07:00
|
|
|
} */
|
2014-01-13 17:32:54 -08:00
|
|
|
|
2014-02-27 15:59:49 -08:00
|
|
|
|
2014-02-28 19:12:59 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
//Read all pool configs from pool_configs and join them with their coin profile
|
|
|
|
var buildPoolConfigs = function(){
|
|
|
|
var configs = {};
|
|
|
|
fs.readdirSync('pool_configs').forEach(function(file){
|
|
|
|
var poolOptions = JSON.parse(JSON.minify(fs.readFileSync('pool_configs/' + file, {encoding: 'utf8'})));
|
|
|
|
if (poolOptions.disabled) return;
|
|
|
|
var coinFilePath = 'coins/' + poolOptions.coin;
|
|
|
|
if (!fs.existsSync(coinFilePath)){
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.error('Master', poolOptions.coin, 'could not find file: ' + coinFilePath);
|
2014-03-09 19:31:58 -07:00
|
|
|
return;
|
|
|
|
}
|
2014-02-28 19:12:59 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
var coinProfile = JSON.parse(JSON.minify(fs.readFileSync(coinFilePath, {encoding: 'utf8'})));
|
|
|
|
poolOptions.coin = coinProfile;
|
|
|
|
configs[poolOptions.coin.name] = poolOptions;
|
|
|
|
});
|
|
|
|
return configs;
|
|
|
|
};
|
2014-02-27 15:59:49 -08:00
|
|
|
|
2014-01-13 17:32:54 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
|
|
|
|
var spawnPoolWorkers = function(portalConfig, poolConfigs){
|
2014-02-28 19:12:59 -08:00
|
|
|
var serializedConfigs = JSON.stringify(poolConfigs);
|
|
|
|
|
2014-02-27 15:59:49 -08:00
|
|
|
|
2014-02-28 19:12:59 -08:00
|
|
|
var numForks = (function(){
|
2014-03-09 19:31:58 -07:00
|
|
|
if (!portalConfig.clustering || !portalConfig.clustering.enabled)
|
2014-02-28 19:12:59 -08:00
|
|
|
return 1;
|
2014-03-09 19:31:58 -07:00
|
|
|
if (portalConfig.clustering.forks === 'auto')
|
2014-02-28 19:12:59 -08:00
|
|
|
return os.cpus().length;
|
2014-03-09 19:31:58 -07:00
|
|
|
if (!portalConfig.clustering.forks || isNaN(portalConfig.clustering.forks))
|
2014-02-28 19:12:59 -08:00
|
|
|
return 1;
|
2014-03-09 19:31:58 -07:00
|
|
|
return portalConfig.clustering.forks;
|
2014-02-28 19:12:59 -08:00
|
|
|
})();
|
2014-01-13 17:32:54 -08:00
|
|
|
|
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
var createPoolWorker = function(forkId){
|
2014-03-06 12:46:01 -08:00
|
|
|
var worker = cluster.fork({
|
2014-03-24 13:00:18 -07:00
|
|
|
workerType: 'pool',
|
|
|
|
forkId: forkId,
|
|
|
|
pools: serializedConfigs,
|
|
|
|
portalConfig: JSON.stringify(portalConfig)
|
2014-03-06 12:46:01 -08:00
|
|
|
});
|
2014-03-09 19:31:58 -07:00
|
|
|
worker.on('exit', function(code, signal){
|
2014-03-22 23:46:59 -07:00
|
|
|
logger.error('Master', 'PoolSpanwer', 'Fork ' + forkId + ' died, spawning replacement worker...');
|
2014-03-11 20:47:14 -07:00
|
|
|
setTimeout(function(){
|
|
|
|
createPoolWorker(forkId);
|
|
|
|
}, 2000);
|
2014-03-09 19:31:58 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-03-22 23:46:59 -07:00
|
|
|
var i = 0;
|
|
|
|
var spawnInterval = setInterval(function(){
|
2014-03-09 19:31:58 -07:00
|
|
|
createPoolWorker(i);
|
2014-03-22 23:46:59 -07:00
|
|
|
i++;
|
|
|
|
if (i === numForks){
|
|
|
|
clearInterval(spawnInterval);
|
|
|
|
logger.debug('Master', 'PoolSpawner', 'Spawned pools for all ' + numForks + ' configured forks');
|
|
|
|
}
|
|
|
|
}, 250);
|
2014-01-13 17:32:54 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
};
|
2014-02-27 15:59:49 -08:00
|
|
|
|
2014-03-01 17:19:10 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
var startWorkerListener = function(poolConfigs){
|
2014-03-22 23:16:06 -07:00
|
|
|
var workerListener = new WorkerListener(logger, poolConfigs);
|
2014-03-04 00:33:55 -08:00
|
|
|
workerListener.init();
|
2014-03-09 19:31:58 -07:00
|
|
|
};
|
2014-03-01 17:19:10 -08:00
|
|
|
|
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
var startBlockListener = function(portalConfig){
|
2014-02-28 19:12:59 -08:00
|
|
|
//block notify options
|
|
|
|
//setup block notify here and use IPC to tell appropriate pools
|
2014-03-09 19:31:58 -07:00
|
|
|
var listener = new BlocknotifyListener(portalConfig.blockNotifyListener);
|
2014-02-28 19:12:59 -08:00
|
|
|
listener.on('log', function(text){
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.debug('Master', 'Blocknotify', text);
|
2014-02-28 19:12:59 -08:00
|
|
|
});
|
|
|
|
listener.on('hash', function(message){
|
2014-01-15 15:04:19 -08:00
|
|
|
|
2014-03-04 12:24:02 -08:00
|
|
|
var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash};
|
2014-02-28 19:12:59 -08:00
|
|
|
Object.keys(cluster.workers).forEach(function(id) {
|
2014-03-04 12:24:02 -08:00
|
|
|
cluster.workers[id].send(ipcMessage);
|
2014-02-28 19:12:59 -08:00
|
|
|
});
|
2014-01-15 15:04:19 -08:00
|
|
|
|
2014-02-28 19:12:59 -08:00
|
|
|
});
|
|
|
|
listener.start();
|
2014-03-09 19:31:58 -07:00
|
|
|
};
|
2014-01-15 15:04:19 -08:00
|
|
|
|
2014-03-14 12:02:55 -07:00
|
|
|
var startRedisBlockListener = function(portalConfig){
|
|
|
|
//block notify options
|
|
|
|
//setup block notify here and use IPC to tell appropriate pools
|
2014-03-15 17:58:28 -07:00
|
|
|
|
|
|
|
if (!portalConfig.redisBlockNotifyListener.enabled) return;
|
|
|
|
|
2014-03-14 12:02:55 -07:00
|
|
|
var listener = new RedisBlocknotifyListener(portalConfig.redisBlockNotifyListener);
|
|
|
|
listener.on('log', function(text){
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.debug('Master', 'blocknotify', text);
|
2014-03-14 12:02:55 -07:00
|
|
|
}).on('hash', function (message) {
|
|
|
|
var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash};
|
|
|
|
Object.keys(cluster.workers).forEach(function(id) {
|
|
|
|
cluster.workers[id].send(ipcMessage);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
listener.start();
|
|
|
|
};
|
|
|
|
|
2014-03-07 15:29:16 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
var startPaymentProcessor = function(poolConfigs){
|
2014-03-26 14:08:34 -07:00
|
|
|
|
|
|
|
var enabledForAny = false;
|
|
|
|
for (var pool in poolConfigs){
|
|
|
|
var p = poolConfigs[pool];
|
|
|
|
var enabled = p.shareProcessing && p.shareProcessing.internal && p.shareProcessing.internal.enabled;
|
|
|
|
if (enabled){
|
|
|
|
enabledForAny = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!enabledForAny)
|
|
|
|
return;
|
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
var worker = cluster.fork({
|
|
|
|
workerType: 'paymentProcessor',
|
|
|
|
pools: JSON.stringify(poolConfigs)
|
|
|
|
});
|
|
|
|
worker.on('exit', function(code, signal){
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.error('Master', 'Payment Processor', 'Payment processor died, spawning replacement...');
|
2014-03-11 20:47:14 -07:00
|
|
|
setTimeout(function(){
|
2014-03-19 13:24:29 -07:00
|
|
|
startPaymentProcessor(poolConfigs);
|
2014-03-12 23:37:27 -07:00
|
|
|
}, 2000);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var startWebsite = function(portalConfig, poolConfigs){
|
2014-03-14 14:56:54 -07:00
|
|
|
|
2014-03-12 23:37:27 -07:00
|
|
|
if (!portalConfig.website.enabled) return;
|
|
|
|
|
|
|
|
var worker = cluster.fork({
|
|
|
|
workerType: 'website',
|
|
|
|
pools: JSON.stringify(poolConfigs),
|
|
|
|
portalConfig: JSON.stringify(portalConfig)
|
|
|
|
});
|
|
|
|
worker.on('exit', function(code, signal){
|
2014-03-22 23:16:06 -07:00
|
|
|
logger.error('Master', 'Website', 'Website process died, spawning replacement...');
|
2014-03-12 23:37:27 -07:00
|
|
|
setTimeout(function(){
|
2014-03-19 13:24:29 -07:00
|
|
|
startWebsite(portalConfig, poolConfigs);
|
2014-03-11 20:47:14 -07:00
|
|
|
}, 2000);
|
2014-03-09 19:31:58 -07:00
|
|
|
});
|
|
|
|
};
|
2014-01-15 15:04:19 -08:00
|
|
|
|
2014-02-28 19:12:59 -08:00
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
(function init(){
|
|
|
|
|
|
|
|
var poolConfigs = buildPoolConfigs();
|
|
|
|
|
|
|
|
spawnPoolWorkers(portalConfig, poolConfigs);
|
|
|
|
|
|
|
|
startPaymentProcessor(poolConfigs);
|
|
|
|
|
|
|
|
startBlockListener(portalConfig);
|
|
|
|
|
2014-03-14 12:02:55 -07:00
|
|
|
startRedisBlockListener(portalConfig);
|
|
|
|
|
2014-03-09 19:31:58 -07:00
|
|
|
startWorkerListener(poolConfigs);
|
|
|
|
|
2014-03-12 23:37:27 -07:00
|
|
|
startWebsite(portalConfig, poolConfigs);
|
2014-03-14 12:02:55 -07:00
|
|
|
|
|
|
|
})();
|