373 lines
9.2 KiB
JavaScript
373 lines
9.2 KiB
JavaScript
/**
|
|
* Cryptonite Node.JS Pool
|
|
* https://github.com/dvandal/cryptonote-nodejs-pool
|
|
*
|
|
* Pool initialization script
|
|
**/
|
|
|
|
// Load needed modules
|
|
var fs = require('fs');
|
|
var cluster = require('cluster');
|
|
var os = require('os');
|
|
|
|
// Load configuration
|
|
require('./lib/configReader.js');
|
|
|
|
// Load log system
|
|
require('./lib/logger.js');
|
|
|
|
// Initialize log system
|
|
var logSystem = 'master';
|
|
require('./lib/exceptionWriter.js')(logSystem);
|
|
|
|
// Pool informations
|
|
log('info', logSystem, 'Starting Cryptonote Node.JS pool version %s', [version]);
|
|
|
|
// Check configuration data
|
|
var poolAddress = config.poolServer.poolAddress || null;
|
|
if (!poolAddress || poolAddress.match(/(\s+|\*)/)) {
|
|
log('error', logSystem, 'Invalid pool wallet address in configuration file (poolServer.poolAddress)');
|
|
process.exit();
|
|
}
|
|
|
|
// Initialize redis database client
|
|
var redis = require('redis');
|
|
|
|
var redisDB = (config.redis.db && config.redis.db > 0) ? config.redis.db : 0;
|
|
global.redisClient = redis.createClient(config.redis.port, config.redis.host, {
|
|
db: redisDB,
|
|
auth_pass: config.redis.auth
|
|
});
|
|
|
|
// Load pool modules
|
|
if (cluster.isWorker) {
|
|
switch (process.env.workerType) {
|
|
case 'pool':
|
|
require('./lib/pool.js');
|
|
break;
|
|
case 'daemon':
|
|
case 'miningSource':
|
|
require('./lib/miningSource.js')
|
|
break
|
|
case 'chainObserver':
|
|
require('./lib/chainObserver.js')
|
|
break
|
|
case 'blockUnlocker':
|
|
require('./lib/blockUnlocker.js');
|
|
break;
|
|
case 'paymentProcessor':
|
|
require('./lib/paymentProcessor.js');
|
|
break;
|
|
case 'api':
|
|
require('./lib/api.js');
|
|
break;
|
|
case 'chartsDataCollector':
|
|
require('./lib/chartsDataCollector.js');
|
|
break;
|
|
case 'telegramBot':
|
|
require('./lib/telegramBot.js');
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Developer donations
|
|
if (devFee < 0.2)
|
|
log('info', logSystem, 'Developer donation \(devDonation\) is set to %d\%, Please consider raising it to 0.2\% or higher !!!', [devFee]);
|
|
|
|
// Run a single module ?
|
|
var singleModule = (function () {
|
|
var validModules = ['pool', 'api', 'unlocker', 'payments', 'chartsDataCollector', 'telegramBot', 'daemon', 'miningSource', 'chainObserver'];
|
|
|
|
for (var i = 0; i < process.argv.length; i++) {
|
|
if (process.argv[i].indexOf('-module=') === 0) {
|
|
var moduleName = process.argv[i].split('=')[1];
|
|
if (validModules.indexOf(moduleName) > -1)
|
|
return moduleName;
|
|
|
|
log('error', logSystem, 'Invalid module "%s", valid modules: %s', [moduleName, validModules.join(', ')]);
|
|
process.exit();
|
|
}
|
|
}
|
|
})();
|
|
|
|
/**
|
|
* Start modules
|
|
**/
|
|
(function init () {
|
|
checkRedisVersion(function () {
|
|
if (singleModule) {
|
|
log('info', logSystem, 'Running in single module mode: %s', [singleModule]);
|
|
|
|
switch (singleModule) {
|
|
case 'daemon':
|
|
case 'miningSource':
|
|
spawnMiningSource();
|
|
break;
|
|
case 'chainObserver':
|
|
spawnChainObserver();
|
|
break;
|
|
case 'pool':
|
|
spawnPoolWorkers();
|
|
break;
|
|
case 'unlocker':
|
|
spawnBlockUnlocker();
|
|
break;
|
|
case 'payments':
|
|
spawnPaymentProcessor();
|
|
break;
|
|
case 'api':
|
|
spawnApi();
|
|
break;
|
|
case 'chartsDataCollector':
|
|
spawnChartsDataCollector();
|
|
break;
|
|
case 'telegramBot':
|
|
spawnTelegramBot();
|
|
break;
|
|
}
|
|
} else {
|
|
spawnPoolWorkers();
|
|
spawnMiningSource();
|
|
spawnChainObserver();
|
|
spawnBlockUnlocker();
|
|
spawnPaymentProcessor();
|
|
spawnApi();
|
|
spawnChartsDataCollector();
|
|
spawnTelegramBot();
|
|
}
|
|
});
|
|
})();
|
|
|
|
/**
|
|
* Check redis database version
|
|
**/
|
|
function checkRedisVersion (callback) {
|
|
redisClient.info(function (error, response) {
|
|
if (error) {
|
|
log('error', logSystem, 'Redis version check failed');
|
|
return;
|
|
}
|
|
var parts = response.split('\r\n');
|
|
var version;
|
|
var versionString;
|
|
for (var i = 0; i < parts.length; i++) {
|
|
if (parts[i].indexOf(':') !== -1) {
|
|
var valParts = parts[i].split(':');
|
|
if (valParts[0] === 'redis_version') {
|
|
versionString = valParts[1];
|
|
version = parseFloat(versionString);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!version) {
|
|
log('error', logSystem, 'Could not detect redis version - must be super old or broken');
|
|
return;
|
|
} else if (version < 2.6) {
|
|
log('error', logSystem, "You're using redis version %s the minimum required version is 2.6. Follow the damn usage instructions...", [versionString]);
|
|
return;
|
|
}
|
|
callback();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn pool workers module
|
|
**/
|
|
function spawnPoolWorkers () {
|
|
if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return;
|
|
|
|
if (config.poolServer.ports.length === 0) {
|
|
log('error', logSystem, 'Pool server enabled but no ports specified');
|
|
return;
|
|
}
|
|
var numForks = (function () {
|
|
if (!config.poolServer.clusterForks)
|
|
return 1;
|
|
if (config.poolServer.clusterForks === 'auto')
|
|
return os.cpus()
|
|
.length;
|
|
if (isNaN(config.poolServer.clusterForks))
|
|
return 1;
|
|
return config.poolServer.clusterForks;
|
|
})();
|
|
|
|
var poolWorkers = {};
|
|
|
|
var createPoolWorker = function (forkId) {
|
|
var worker = cluster.fork({
|
|
workerType: 'pool',
|
|
forkId: forkId
|
|
});
|
|
worker.forkId = forkId;
|
|
worker.type = 'pool';
|
|
poolWorkers[forkId] = worker;
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'Pool fork %s died, spawning replacement worker...', [forkId]);
|
|
setTimeout(function () {
|
|
createPoolWorker(forkId);
|
|
}, 2000);
|
|
})
|
|
.on('message', function (msg) {
|
|
switch (msg.type) {
|
|
case 'banIP':
|
|
Object.keys(cluster.workers)
|
|
.forEach(function (id) {
|
|
if (cluster.workers[id].type === 'pool') {
|
|
cluster.workers[id].send({
|
|
type: 'banIP',
|
|
ip: msg.ip
|
|
});
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
});
|
|
};
|
|
|
|
var i = 1;
|
|
var spawnInterval = setInterval(function () {
|
|
createPoolWorker(i.toString());
|
|
i++;
|
|
if (i - 1 === numForks) {
|
|
clearInterval(spawnInterval);
|
|
log('info', logSystem, 'Pool spawned on %d thread(s)', [numForks]);
|
|
}
|
|
}, 10);
|
|
}
|
|
|
|
/**
|
|
* Spawn mining source module
|
|
**/
|
|
function spawnMiningSource () {
|
|
if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'miningSource'
|
|
});
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'Mining source died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnMiningSource();
|
|
}, 10);
|
|
})
|
|
.on('message', function (msg) {
|
|
switch (msg.type) {
|
|
case 'BlockTemplate':
|
|
Object.keys(cluster.workers)
|
|
.forEach(function (id) {
|
|
if (cluster.workers[id].type === 'pool') {
|
|
cluster.workers[id].send({
|
|
type: 'BlockTemplate',
|
|
block: msg.block
|
|
});
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn chain observer module
|
|
**/
|
|
function spawnChainObserver () {
|
|
if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'chainObserver'
|
|
});
|
|
worker.on('exit', function () {
|
|
log('error', logSystem, 'Chain observer died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnChainObserver();
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn block unlocker module
|
|
**/
|
|
function spawnBlockUnlocker () {
|
|
if (!config.blockUnlocker || !config.blockUnlocker.enabled) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'blockUnlocker'
|
|
});
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'Block unlocker died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnBlockUnlocker();
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn payment processor module
|
|
**/
|
|
function spawnPaymentProcessor () {
|
|
if (!config.payments || !config.payments.enabled) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'paymentProcessor'
|
|
});
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'Payment processor died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnPaymentProcessor();
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn API module
|
|
**/
|
|
function spawnApi () {
|
|
if (!config.api || !config.api.enabled) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'api'
|
|
});
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'API died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnApi();
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn charts data collector module
|
|
**/
|
|
function spawnChartsDataCollector () {
|
|
if (!config.charts) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'chartsDataCollector'
|
|
});
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'chartsDataCollector died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnChartsDataCollector();
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spawn telegram bot module
|
|
**/
|
|
function spawnTelegramBot () {
|
|
if (!config.telegram || !config.telegram.enabled || !config.telegram.token) return;
|
|
|
|
var worker = cluster.fork({
|
|
workerType: 'telegramBot'
|
|
});
|
|
worker.on('exit', function (code, signal) {
|
|
log('error', logSystem, 'telegramBot died, spawning replacement...');
|
|
setTimeout(function () {
|
|
spawnTelegramBot();
|
|
}, 2000);
|
|
});
|
|
}
|