diff --git a/coins/hdac.json b/coins/hdac.json new file mode 100644 index 000000000..079ca1a37 --- /dev/null +++ b/coins/hdac.json @@ -0,0 +1,11 @@ +{ + "name": "HDAC", + "symbol": "DAC", + "algorithm": "lyra2rev2", + /** + * @Hdac + * Add the 'reward' key value for ePoW, a unique consensus algorithm of Hdac. + * This annotation should be removed when reflecting on the server due to the JSON formatting convention. + */ + "reward": "ePoW" +} \ No newline at end of file diff --git a/config-hdac.json b/config-hdac.json new file mode 100644 index 000000000..207cacc7f --- /dev/null +++ b/config-hdac.json @@ -0,0 +1,121 @@ +{ + "logLevel": "debug", + "logColors": true, + + "cliHost": "127.0.0.1", + "cliPort": 17117, + + "clustering": { + "enabled": true, + "forks": "auto" + }, + + /** + * @Hdac + * Add the 'blockWindowSizeRefreshInterval' key value. + * This is used to set the schedule period associated with ePOW application. + * This annotation should be removed when reflecting on the server due to the JSON formatting convention. + */ + "defaultPoolConfigs": { + "blockRefreshInterval": 1000, + "blockWindowSizeRefreshInterval": 50000, + "jobRebroadcastTimeout": 55, + "connectionTimeout": 600, + "emitInvalidBlockHashes": false, + "validateWorkerUsername": true, + "tcpProxyProtocol": false, + "banning": { + "enabled": false, + "time": 600, + "invalidPercent": 50, + "checkThreshold": 500, + "purgeInterval": 300 + }, + "redis": { + "host": "127.0.0.1", + "port": 6379 + } + }, + + "website": { + "enabled": true, + "host": "0.0.0.0", + "port": 8088, + "stratumHost": "0.0.0.0", + "stats": { + "updateInterval": 60, + "historicalRetention": 43200, + "hashrateWindow": 300 + }, + "adminCenter": { + "enabled": true, + "password": "password" + } + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379 + }, + + "switching": { + "switch1": { + "enabled": false, + "algorithm": "sha256", + "ports": { + "3333": { + "diff": 10, + "varDiff": { + "minDiff": 16, + "maxDiff": 512, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + } + } + }, + "switch2": { + "enabled": false, + "algorithm": "scrypt", + "ports": { + "4444": { + "diff": 10, + "varDiff": { + "minDiff": 16, + "maxDiff": 512, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + } + } + }, + "switch3": { + "enabled": false, + "algorithm": "x11", + "ports": { + "5555": { + "diff": 0.001, + "varDiff": { + "minDiff": 0.001, + "maxDiff": 1, + "targetTime": 15, + "retargetTime": 60, + "variancePercent": 30 + } + } + } + } + }, + + "profitSwitch": { + "enabled": false, + "updateInterval": 600, + "depth": 0.90, + "usePoloniex": true, + "useCryptsy": true, + "useMintpal": true, + "useBittrex": true + } +} diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js index cedd542c5..315f886f7 100644 --- a/libs/paymentProcessor.js +++ b/libs/paymentProcessor.js @@ -75,9 +75,23 @@ function SetupForPool(logger, poolOptions, setupFinished) { } else if (!result.response || !result.response.ismine) { logger.error(logSystem, logComponent, - 'Daemon does not own pool address - payment processing can not be done with this daemon, ' + 'Validateaddress failed, trying for newer getaddressinfo Command ..., ' + JSON.stringify(result.response)); - callback(true); + daemon.cmd('getaddressinfo', [poolOptions.address], function(result) { + if (result.error){ + logger.error(logSystem, logComponent, 'Error with payment processing daemon, getaddressinfo failed ... ' + JSON.stringify(result.error)); + callback(true); + } + else if (!result.response || !result.response.ismine) { + logger.error(logSystem, logComponent, + 'Daemon does not own pool address - payment processing can not be done with this daemon, ' + + JSON.stringify(result.response)); + callback(true); + } + else{ + callback() + } + }, true); } else { callback() @@ -234,16 +248,16 @@ function SetupForPool(logger, poolOptions, setupFinished) { round.category = 'kicked'; return; } - else if (!tx.result.details || (tx.result.details && tx.result.details.length === 0)) { - logger.warning(logSystem, logComponent, 'Daemon reports no details for transaction: ' + round.txHash); - round.category = 'kicked'; - return; - } else if (tx.error || !tx.result) { logger.error(logSystem, logComponent, 'Odd error with gettransaction ' + round.txHash + ' ' + JSON.stringify(tx)); return; } + else if (!tx.result.details || (tx.result.details && tx.result.details.length === 0)) { + logger.warning(logSystem, logComponent, 'Daemon reports no details for transaction: ' + round.txHash); + round.category = 'kicked'; + return; + } var generationTx = tx.result.details.filter(function (tx) { return tx.address === poolOptions.address; diff --git a/libs/poolWorker.js b/libs/poolWorker.js index 67df9043a..f06708271 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -151,7 +151,7 @@ module.exports = function(logger){ else { pool.daemon.cmd('validateaddress', [workerName], function (results) { var isValid = results.filter(function (r) { - return r.response.isvalid + return r.response && r.response.isvalid; }).length > 0; authCallback(isValid); }); diff --git a/package.json b/package.json index 597f6f26e..e76157fef 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "nonce": "^1.0.4", "redis": "^2.7.1", "request": "^2.83.0", - "stratum-pool": "git://github.com/foxer666/node-stratum-pool.git" + "stratum-pool": "git://github.com/OpenCommunityCoin/node-stratum-pool.git" }, "engines": { "node": ">=8.1.4" diff --git a/pay.js b/pay.js new file mode 100644 index 000000000..fb5ac0052 --- /dev/null +++ b/pay.js @@ -0,0 +1,216 @@ +var fs = require('fs'); +var path = require('path'); +var os = require('os'); +var cluster = require('cluster'); + +var async = require('async'); + +var PoolLogger = require('./libs/logUtil.js'); +var PaymentProcessor = require('./libs/paymentProcessor.js'); + +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'}))); +var poolConfigs; + + +var logger = new PoolLogger({ + logLevel: portalConfig.logLevel, + logColors: portalConfig.logColors +}); + + + + +try { + require('newrelic'); + if (cluster.isMaster) + logger.debug('NewRelic', 'Monitor', 'New Relic initiated'); +} catch(e) {} + + +//Try to give process ability to handle 100k concurrent connections +try{ + 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'); + } + finally { + // Find out which user used sudo through the environment variable + var uid = parseInt(process.env.SUDO_UID); + // Set our server's uid to that user + if (uid) { + process.setuid(uid); + logger.debug('POSIX', 'Connection Limit', 'Raised to 100K concurrent connections, now running as non-root user: ' + process.getuid()); + } + } +} +catch(e){ + if (cluster.isMaster) + logger.debug('POSIX', 'Connection Limit', '(Safe to ignore) POSIX module not installed and resource (connection) limit was not raised'); +} + + +if (cluster.isWorker){ + + switch(process.env.workerType){ + case 'pool': + new PoolWorker(logger); + break; + case 'paymentProcessor': + new PaymentProcessor(logger); + break; + case 'website': + new Website(logger); + break; + case 'profitSwitch': + new ProfitSwitch(logger); + break; + } + + return; +} + + +//Read all pool configs from pool_configs and join them with their coin profile +var buildPoolConfigs = function(){ + var configs = {}; + var configDir = 'payment_configs/'; + + var poolConfigFiles = []; + + + /* Get filenames of pool config json files that are enabled */ + fs.readdirSync(configDir).forEach(function(file){ + if (!fs.existsSync(configDir + file) || path.extname(configDir + file) !== '.json') return; + var poolOptions = JSON.parse(JSON.minify(fs.readFileSync(configDir + file, {encoding: 'utf8'}))); + if (!poolOptions.enabled) return; + poolOptions.fileName = file; + poolConfigFiles.push(poolOptions); + }); + + + /* Ensure no pool uses any of the same ports as another pool */ + for (var i = 0; i < poolConfigFiles.length; i++){ + var ports = Object.keys(poolConfigFiles[i].ports); + for (var f = 0; f < poolConfigFiles.length; f++){ + if (f === i) continue; + var portsF = Object.keys(poolConfigFiles[f].ports); + for (var g = 0; g < portsF.length; g++){ + if (ports.indexOf(portsF[g]) !== -1){ + logger.error('Master', poolConfigFiles[f].fileName, 'Has same configured port of ' + portsF[g] + ' as ' + poolConfigFiles[i].fileName); + process.exit(1); + return; + } + } + + if (poolConfigFiles[f].coin === poolConfigFiles[i].coin){ + logger.error('Master', poolConfigFiles[f].fileName, 'Pool has same configured coin file coins/' + poolConfigFiles[f].coin + ' as ' + poolConfigFiles[i].fileName + ' pool'); + process.exit(1); + return; + } + + } + } + + + poolConfigFiles.forEach(function(poolOptions){ + + poolOptions.coinFileName = poolOptions.coin; + + var coinFilePath = 'coins/' + poolOptions.coinFileName; + if (!fs.existsSync(coinFilePath)){ + logger.error('Master', poolOptions.coinFileName, 'could not find file: ' + coinFilePath); + return; + } + + var coinProfile = JSON.parse(JSON.minify(fs.readFileSync(coinFilePath, {encoding: 'utf8'}))); + poolOptions.coin = coinProfile; + poolOptions.coin.name = poolOptions.coin.name.toLowerCase(); + + if (poolOptions.coin.name in configs){ + + logger.error('Master', poolOptions.fileName, 'coins/' + poolOptions.coinFileName + + ' has same configured coin name ' + poolOptions.coin.name + ' as coins/' + + configs[poolOptions.coin.name].coinFileName + ' used by pool config ' + + configs[poolOptions.coin.name].fileName); + + process.exit(1); + return; + } + + for (var option in portalConfig.defaultPoolConfigs){ + if (!(option in poolOptions)){ + var toCloneOption = portalConfig.defaultPoolConfigs[option]; + var clonedOption = {}; + if (toCloneOption.constructor === Object) { + Object.assign(clonedOption, toCloneOption); + } else { + clonedOption = toCloneOption; + } + poolOptions[option] = clonedOption; + } + } + + + configs[poolOptions.coin.name] = poolOptions; + + if (!(coinProfile.algorithm in algos)){ + logger.error('Master', coinProfile.name, 'Cannot run a pool for unsupported algorithm "' + coinProfile.algorithm + '"'); + delete configs[poolOptions.coin.name]; + } + + }); + return configs; +}; + + + +var startPaymentProcessor = function(){ + + var enabledForAny = false; + for (var pool in poolConfigs){ + var p = poolConfigs[pool]; + var enabled = p.enabled && p.paymentProcessing && p.paymentProcessing.enabled; + if (enabled){ + enabledForAny = true; + break; + } + } + + console.log(poolConfigs); + + if (!enabledForAny) + return; + + var worker = cluster.fork({ + workerType: 'paymentProcessor', + pools: JSON.stringify(poolConfigs) + }); + worker.on('exit', function(code, signal){ + logger.error('Master', 'Payment Processor', 'Payment processor died, spawning replacement...'); + setTimeout(function(){ + startPaymentProcessor(poolConfigs); + }, 2000); + }); +}; + + +(function init(){ + + poolConfigs = buildPoolConfigs(); + + startPaymentProcessor(); + +})(); diff --git a/pool_configs/h-dac.json b/pool_configs/h-dac.json new file mode 100644 index 000000000..317f1ee7c --- /dev/null +++ b/pool_configs/h-dac.json @@ -0,0 +1,78 @@ +{ + "enabled": true, + "coin": "hdac.json", + + "address": "HBSREYHSZUoLELycQcpGTFnwYPcbvfeywx", + + "rewardRecipients": { + "HBSREYHSZUoLELycQcpGTFnwYPcbvfeywx": 1 + }, + + "paymentProcessing": { + "enabled": false, + "paymentInterval": 20, + "minimumPayment": 70, + "daemon": { + "host": "127.0.0.1", + "port": "8822", + "user": "bab", + "password": "foobar123" + } + }, + + "ports": { + "3008": { + "diff": 32.00000 + }, + "3333": { + "diff": 64.00000, + "varDiff": { + "minDiff": 64.00000, + "maxDiff": 256, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + }, + "3256": { + "diff": 128 + } + }, + + "daemons": [ + { + "host": "127.0.0.1", + "port": "8822", + "user": "bab", + "password": "foobar123" + } + ], + + "p2p": { + "enabled": false, + "host": "127.0.0.1", + "port": 19333, + "disableTransactions": true + }, + + "mposMode": { + "enabled": false, + "host": "set your mysql host", + "port": 3306, + "user": "set your mysql user id", + "password": "set your mysql password", + "database": "mpos", + "checkPassword": true, + "autoCreateWorker": false + }, + + "mongoMode": { + "enabled": false, + "host": "127.0.0.1", + "user": "", + "pass": "", + "database": "hdac", + "authMechanism": "DEFAULT" + } + +}