diff --git a/init.js b/init.js index decf2cb..9cd58b2 100644 --- a/init.js +++ b/init.js @@ -4,7 +4,6 @@ var os = require('os'); var cluster = require('cluster'); var async = require('async'); -var posix = require('posix'); var PoolLogger = require('./libs/logUtil.js'); var BlocknotifyListener = require('./libs/blocknotifyListener.js'); var RedisBlocknotifyListener = require('./libs/redisblocknotifyListener.js'); @@ -17,6 +16,11 @@ var algos = require('stratum-pool/lib/algoProperties.js'); JSON.minify = JSON.minify || require("node-json-minify"); +if (!fs.existsSync('config.json')){ + console.log('config.json file does not exist. Read the installation/setup instructions.'); + return; +} + var portalConfig = JSON.parse(JSON.minify(fs.readFileSync("config.json", {encoding: 'utf8'}))); @@ -36,10 +40,18 @@ try { //Try to give process ability to handle 100k concurrent connections try{ - posix.setrlimit('nofile', { soft: 100000, hard: 100000 }); + var posix = require('posix'); + try { + posix.setrlimit('nofile', { soft: 100000, hard: 100000 }); + } + catch(e){ + if (cluster.isMaster) + logger.warning('POSIX', 'Connection Limit', '(Safe to ignore) Must be ran as root to increase resource limits'); + } } catch(e){ - logger.warning('POSIX', 'Connection Limit', '(Safe to ignore) Must be ran as root to increase resource limits'); + if (cluster.isMaster) + logger.debug('POSIX', 'Connection Limit', '(Safe to ignore) POSIX module not installed and resource (connection) limit was not raised'); } diff --git a/libs/blocknotifyListener.js b/libs/blocknotifyListener.js index 921e1f4..4691c13 100644 --- a/libs/blocknotifyListener.js +++ b/libs/blocknotifyListener.js @@ -32,7 +32,16 @@ var listener = module.exports = function listener(options){ emitLog('Block listener connection ended'); - var message = JSON.parse(data); + var message; + + try{ + message = JSON.parse(data); + } + catch(e){ + emitLog('Block listener failed to parse message ' + data); + return; + } + if (message.password === options.password) { _this.emit('hash', message); } @@ -42,7 +51,7 @@ var listener = module.exports = function listener(options){ }); } catch(e){ - emitLog('Block listener failed to parse message ' + data); + emitLog('Block listener had an error: ' + e); } }); diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js index 11ddc7f..3d3482a 100644 --- a/libs/paymentProcessor.js +++ b/libs/paymentProcessor.js @@ -52,6 +52,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ var logSystem = 'Payments'; var logComponent = coin; + var processingPayments = true; var daemon; var redisClient; @@ -119,8 +120,28 @@ function SetupForPool(logger, poolOptions, setupFinished){ }); - - + /* Call redis to check if previous sendmany and/or redis cleanout commands completed successfully. + If sendmany worked fine but redis commands failed you HAVE TO run redis commands again + (manually) to prevent double payments. If sendmany failed too you can safely delete + coin + '_finalRedisCommands' string from redis to let pool calculate payments again. */ + function checkPreviousPaymentsStatus(callback) { + redisClient.get(coin + '_finalRedisCommands', function(error, reply) { + if (error){ + callback('Could not get finalRedisCommands - ' + JSON.stringify(error)); + return; + } + if (reply) { + callback('Payments stopped because of the critical error - failed commands saved in ' + + coin + '_finalRedisCommands redis set:\n' + reply); + return; + } else { + /* There was no error in previous sendmany and/or redis cleanout commands + so we can safely continue */ + processingPayments = false; + callback(); + } + }); + } /* Number.toFixed gives us the decimal places we want, but as a string. parseFloat turns it back into number @@ -140,6 +161,21 @@ function SetupForPool(logger, poolOptions, setupFinished){ async.waterfall([ + function(callback) { + if (processingPayments) { + checkPreviousPaymentsStatus(function(error){ + if (error) { + logger.error(logSystem, logComponent, error); + callback('Check finished - previous payments processing error'); + return; + } + callback(); + }); + return; + } + callback(); + }, + /* Call redis to get an array of rounds - which are coinbase transactions and block heights from submitted blocks. */ function(callback){ @@ -147,7 +183,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ redisClient.smembers(coin + '_blocksPending', function(error, results){ if (error){ - logger.error(logSystem, logComponent, 'Could get blocks from redis ' + JSON.stringify(error)); + logger.error(logSystem, logComponent, 'Could not get blocks from redis ' + JSON.stringify(error)); callback('Check finished - redis error for getting blocks'); return; } @@ -470,27 +506,35 @@ function SetupForPool(logger, poolOptions, setupFinished){ if (orphanMergeCommands.length > 0) finalRedisCommands = finalRedisCommands.concat(orphanMergeCommands); - if (balanceUpdateCommands.length > 0) finalRedisCommands = finalRedisCommands.concat(balanceUpdateCommands); - if (workerPayoutsCommand.length > 0) finalRedisCommands = finalRedisCommands.concat(workerPayoutsCommand); - if (roundsToDelete.length > 0) finalRedisCommands.push(['del'].concat(roundsToDelete)); - if (toBePaid !== 0) finalRedisCommands.push(['hincrbyfloat', coin + '_stats', 'totalPaid', (toBePaid / magnitude).toFixed(coinPrecision)]); + finalRedisCommands.push(['del', coin + '_finalRedisCommands']); + finalRedisCommands.push(['bgsave']); callback(null, magnitude, workerPayments, finalRedisCommands); + }); + }, + function(magnitude, workerPayments, finalRedisCommands, callback) { + /* Save final redis cleanout commands in case something goes wrong during payments */ + redisClient.set(coin + '_finalRedisCommands', JSON.stringify(finalRedisCommands), function(error, reply) { + if (error){ + callback('Check finished - error with saving finalRedisCommands' + JSON.stringify(error)); + return; + } + callback(null, magnitude, workerPayments, finalRedisCommands); }); }, @@ -503,6 +547,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ callback('Error with final redis commands for cleaning up ' + JSON.stringify(error)); return; } + processingPayments = false; logger.debug(logSystem, logComponent, 'Payments processing performed an interval'); }); }; @@ -524,6 +569,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ logger.debug(logSystem, logComponent, 'Payments to be sent to: ' + JSON.stringify(addressAmounts)); + processingPayments = true; daemon.cmd('sendmany', ['', addressAmounts], function(results){ if (results[0].error){ diff --git a/package.json b/package.json index 5ebd0ec..24e73b1 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,9 @@ "url": "https://github.com/zone117x/node-open-mining-portal.git" }, "dependencies": { - "stratum-pool": "https://github.com/zone117x/node-stratum-pool/archive/master.tar.gz", + "stratum-pool": "git://github.com/zone117x/node-stratum-pool.git", "dateformat": "*", "node-json-minify": "*", - "posix": "*", "redis": "*", "mysql": "*", "async": "*",