diff --git a/README.md b/README.md index ac8eec5..7b8f582 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,8 @@ If your pool uses NOMP let us know and we will list your website here. * http://kryptochaos.com * http://pool.uberpools.org * http://onebtcplace.com -* https://minr.es +* http://minr.es +* http://mining.theminingpools.com Usage ===== @@ -166,7 +167,12 @@ Explanation for each field: /* Specifies the level of log output verbosity. Anything more severy than the level specified will also be logged. */ "logLevel": "debug", //or "warning", "error" - + + + /* The NOMP CLI (command-line interface) will listen for commands on this port. For example, + blocknotify messages are sent to NOMP through this. */ + "cliPort": 17117, + /* By default 'forks' is set to "auto" which will spawn one process/fork/worker for each CPU core in your system. Each of these workers will run a separate instance of your pool(s), and the kernel will load balance miners using these forks. Optionally, the 'forks' field @@ -205,33 +211,12 @@ Explanation for each field: "port": 6379 }, - /* With this enabled, the master process listen on the configured port for messages from the - 'scripts/blockNotify.js' script which your coin daemons can be configured to run when a - new block is available. When a blocknotify message is received, the master process uses - IPC (inter-process communication) to notify each thread about the message. Each thread - then sends the message to the appropriate coin pool. See "Setting up blocknotify" below to - set up your daemon to use this feature. */ - "blockNotifyListener": { - "enabled": true, - "port": 8117, - "password": "test" - }, - - /* With this enabled, the master process will listen on the configured port for messages from - the 'scripts/coinSwitch.js' script which will trigger your proxy pools to switch to the - specified coin (non-case-sensitive). This setting is used in conjuction with the proxy - feature below. */ - "coinSwitchListener": { - "enabled": false, - "port": 8118, - "password": "test" - }, /* In a proxy configuration, you can setup ports that accept miners for work based on a specific algorithm instead of a specific coin. Miners that connect to these ports are automatically switched a coin determined by the server. The default coin is the first configured pool for each algorithm and coin switching can be triggered using the - coinSwitch.js script in the scripts folder. + cli.js script in the scripts folder. Please note miner address authentication must be disabled when using NOMP in a proxy configuration and that payout processing is left up to the server administrator. */ @@ -505,11 +490,11 @@ For more information on these configuration options see the [pool module documen 1. In `config.json` set the port and password for `blockNotifyListener` 2. In your daemon conf file set the `blocknotify` command to use: ``` -node [path to scripts/blockNotify.js] [listener host]:[listener port] [listener password] [coin name in config] %s +node [path to cli.js] [coin name in config] [block hash symbol] ``` Example: inside `dogecoin.conf` add the line ``` -blocknotify=node scripts/blockNotify.js 127.0.0.1:8117 mySuperSecurePassword dogecoin %s +blocknotify=node /home/nomp/scripts/cli.js blocknotify dogecoin %s ``` Alternatively, you can use a more efficient block notify script written in pure C. Build and usage instructions diff --git a/coins/ecoin.json b/coins/ecoin.json deleted file mode 100644 index 9703edf..0000000 --- a/coins/ecoin.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Ecoin", - "symbol": "ECN", - "algorithm": "keccak", - "normalHashing": true, - "diffShift": 32 -} \ No newline at end of file diff --git a/config_example.json b/config_example.json index 869b3db..bc09c9b 100644 --- a/config_example.json +++ b/config_example.json @@ -1,6 +1,8 @@ { "logLevel": "debug", + "cliPort": 17117, + "clustering": { "enabled": true, "forks": "auto" @@ -26,47 +28,47 @@ "port": 6379 }, - "blockNotifyListener": { - "enabled": false, - "port": 8117, - "password": "test" - }, - - "coinSwitchListener": { - "enabled": false, - "host": "127.0.0.1", - "port": 8118, - "password": "test" - }, - - "proxy": { - "sha256": { + "switching": { + "switch1": { "enabled": false, - "port": "3333", - "diff": 10, - "varDiff": { - "minDiff": 16, - "maxDiff": 512, - "targetTime": 15, - "retargetTime": 90, - "variancePercent": 30 + "algorithm": "sha256", + "ports": { + "3333": { + "diff": 10, + "varDiff": { + "minDiff": 16, + "maxDiff": 512, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + } } }, - "scrypt": { + "switch2": { "enabled": false, - "port": "4444", - "diff": 10, - "varDiff": { - "minDiff": 16, - "maxDiff": 512, - "targetTime": 15, - "retargetTime": 90, - "variancePercent": 30 + "algorithm": "scrypt", + "ports": { + "4444": { + "diff": 10, + "varDiff": { + "minDiff": 16, + "maxDiff": 512, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + } } }, - "scrypt-n": { + "switch3": { "enabled": false, - "port": "5555" + "algorithm": "x11", + "ports": { + "5555": { + "diff": 0.001 + } + } } }, diff --git a/init.js b/init.js index 6ff3e74..4d086ce 100644 --- a/init.js +++ b/init.js @@ -5,8 +5,7 @@ var cluster = require('cluster'); var async = require('async'); var PoolLogger = require('./libs/logUtil.js'); -var BlocknotifyListener = require('./libs/blocknotifyListener.js'); -var CoinswitchListener = require('./libs/coinswitchListener.js'); +var CliListener = require('./libs/cliListener.js'); var RedisBlocknotifyListener = require('./libs/redisblocknotifyListener.js'); var PoolWorker = require('./libs/poolWorker.js'); var PaymentProcessor = require('./libs/paymentProcessor.js'); @@ -82,18 +81,68 @@ if (cluster.isWorker){ var buildPoolConfigs = function(){ var configs = {}; var configDir = 'pool_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; - var coinFilePath = 'coins/' + poolOptions.coin; + 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.coin, 'could not find file: ' + 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; + + 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; + } + configs[poolOptions.coin.name] = poolOptions; if (!(coinProfile.algorithm in algos)){ @@ -130,6 +179,10 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ return; } + for (var p in poolConfigs){ + + } + var serializedConfigs = JSON.stringify(poolConfigs); var numForks = (function(){ @@ -185,25 +238,34 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ }; -var startBlockListener = function(portalConfig){ - //block notify options - //setup block notify here and use IPC to tell appropriate pools - var listener = new BlocknotifyListener(portalConfig.blockNotifyListener); +var startCliListener = function(cliPort){ + var listener = new CliListener(cliPort); listener.on('log', function(text){ - logger.debug('Master', 'Blocknotify', text); - }); - listener.on('hash', function(message){ + logger.debug('Master', 'CLI', text); + }).on('command', function(command, params, options){ - var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash}; - Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].send(ipcMessage); - }); + switch(command){ + case 'blocknotify': + Object.keys(cluster.workers).forEach(function(id) { + cluster.workers[id].send({type: 'blocknotify', coin: params[0], hash: params[1]}); + }); + break; + case 'coinswitch': + Object.keys(cluster.workers).forEach(function(id) { + cluster.workers[id].send({type: 'coinswitch', switchName: params[0], coin: params[1] }); + }); + break; + case 'restartpool': + Object.keys(cluster.workers).forEach(function(id) { + cluster.workers[id].send({type: 'restartpool', coin: params[0] }); + }); + } - }); - listener.start(); + console.log('command: ' + JSON.stringify([command, params, options])); + }).start(); }; - +/* // // Receives authenticated events from coin switch listener and triggers proxy // to swtich to a new coin. @@ -219,7 +281,7 @@ var startCoinswitchListener = function(portalConfig){ cluster.workers[id].send(ipcMessage); }); var ipcMessage = { - type:'switch', + type:'coinswitch', coin: message.coin }; Object.keys(cluster.workers).forEach(function(id) { @@ -228,6 +290,7 @@ var startCoinswitchListener = function(portalConfig){ }); listener.start(); }; +*/ var startRedisBlockListener = function(portalConfig){ //block notify options @@ -324,14 +387,12 @@ var startProfitSwitch = function(portalConfig, poolConfigs){ startPaymentProcessor(poolConfigs); - startBlockListener(portalConfig); - - startCoinswitchListener(portalConfig); - startRedisBlockListener(portalConfig); startWebsite(portalConfig, poolConfigs); startProfitSwitch(portalConfig, poolConfigs); + startCliListener(portalConfig.cliPort); + })(); diff --git a/libs/blocknotifyListener.js b/libs/blocknotifyListener.js deleted file mode 100644 index 4691c13..0000000 --- a/libs/blocknotifyListener.js +++ /dev/null @@ -1,69 +0,0 @@ -var events = require('events'); -var net = require('net'); - -var listener = module.exports = function listener(options){ - - var _this = this; - - var emitLog = function(text){ - _this.emit('log', text); - }; - - - this.start = function(){ - if (!options || !options.enabled){ - emitLog('Blocknotify listener disabled'); - return; - } - - var blockNotifyServer = net.createServer(function(c) { - - emitLog('Block listener has incoming connection'); - var data = ''; - try { - c.on('data', function (d) { - emitLog('Block listener received blocknotify data'); - data += d; - if (data.slice(-1) === '\n') { - c.end(); - } - }); - c.on('end', function () { - - emitLog('Block listener connection ended'); - - 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); - } - else - emitLog('Block listener received notification with incorrect password'); - - }); - } - catch(e){ - emitLog('Block listener had an error: ' + e); - } - - }); - blockNotifyServer.listen(options.port, function() { - emitLog('Block notify listener server started on port ' + options.port) - }); - - emitLog("Block listener is enabled, starting server on port " + options.port); - } - - - -}; - -listener.prototype.__proto__ = events.EventEmitter.prototype; diff --git a/libs/cliListener.js b/libs/cliListener.js new file mode 100644 index 0000000..96744b5 --- /dev/null +++ b/libs/cliListener.js @@ -0,0 +1,40 @@ +var events = require('events'); +var net = require('net'); + +var listener = module.exports = function listener(port){ + + var _this = this; + + var emitLog = function(text){ + _this.emit('log', text); + }; + + + this.start = function(){ + net.createServer(function(c) { + + var data = ''; + try { + c.on('data', function (d) { + data += d; + if (data.slice(-1) === '\n') { + c.end(); + } + }); + c.on('end', function () { + var message = JSON.parse(data); + _this.emit('command', message.command, message.params, message.options); + }); + } + catch(e){ + emitLog('CLI listener failed to parse message ' + data); + } + + }).listen(port, '127.0.0.1', function() { + emitLog('CLI listening on port ' + port) + }); + } + +}; + +listener.prototype.__proto__ = events.EventEmitter.prototype; diff --git a/libs/coinswitchListener.js b/libs/coinswitchListener.js deleted file mode 100644 index fad0d2f..0000000 --- a/libs/coinswitchListener.js +++ /dev/null @@ -1,56 +0,0 @@ -var events = require('events'); -var net = require('net'); - -var listener = module.exports = function listener(options){ - - var _this = this; - - var emitLog = function(text){ - _this.emit('log', text); - }; - - - this.start = function(){ - if (!options || !options.enabled){ - emitLog('Coinswitch listener disabled'); - return; - } - - var coinswitchServer = net.createServer(function(c) { - - emitLog('Coinswitch listener has incoming connection'); - var data = ''; - try { - c.on('data', function (d) { - emitLog('Coinswitch listener received switch request'); - data += d; - if (data.slice(-1) === '\n') { - c.end(); - } - }); - c.on('end', function () { - - var message = JSON.parse(data); - if (message.password === options.password) { - _this.emit('switchcoin', message); - } - else - emitLog('Coinswitch listener received notification with incorrect password'); - - }); - } - catch(e){ - emitLog('Coinswitch listener failed to parse message ' + data); - } - - }); - coinswitchServer.listen(options.port, function() { - emitLog('Coinswitch notify listener server started on port ' + options.port) - }); - - emitLog("Coinswitch listener is enabled, starting server on port " + options.port); - } - -}; - -listener.prototype.__proto__ = events.EventEmitter.prototype; diff --git a/libs/poolWorker.js b/libs/poolWorker.js index b1a5d47..5c66279 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -18,6 +18,8 @@ module.exports = function(logger){ var proxySwitch = {}; + var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host); + //Handle messages from master process sent via IPC process.on('message', function(message) { switch(message.type){ @@ -42,26 +44,39 @@ module.exports = function(logger){ break; // IPC message for pool switching - case 'switch': + case 'coinswitch': var logSystem = 'Proxy'; var logComponent = 'Switch'; var logSubCat = 'Thread ' + (parseInt(forkId) + 1); + var switchName = message.switchName; + if (!portalConfig.switching[switchName]) { + logger.error(logSystem, logComponent, logSubCat, 'Switching key not recognized: ' + switchName); + } + var messageCoin = message.coin.toLowerCase(); var newCoin = Object.keys(pools).filter(function(p){ return p.toLowerCase() === messageCoin; })[0]; if (!newCoin){ - logger.debug(logSystem, logComponent, logSubCat, 'Switch message to coin that is not recognized: ' + messageCoin); + logger.error(logSystem, logComponent, logSubCat, 'Switch message to coin that is not recognized: ' + messageCoin); break; } var algo = poolConfigs[newCoin].coin.algorithm; + + if (algo !== proxySwitch[switchName].algorithm){ + logger.error(logSystem, logComponent, logSubCat, 'Cannot switch a ' + + proxySwitch[switchName].algorithm + + ' algo pool to coin ' + newCoin + ' with ' + algo + ' algo'); + break; + } + var newPool = pools[newCoin]; - var oldCoin = proxySwitch[algo].currentPool; + var oldCoin = proxySwitch[switchName].currentPool; var oldPool = pools[oldCoin]; - var proxyPort = proxySwitch[algo].port; + var proxyPorts = Object.keys(proxySwitch[switchName].ports); if (newCoin == oldCoin) { logger.debug(logSystem, logComponent, logSubCat, 'Switch message would have no effect - ignoring ' + newCoin); @@ -74,25 +89,23 @@ module.exports = function(logger){ oldPool.relinquishMiners( function (miner, cback) { // relinquish miners that are attached to one of the "Auto-switch" ports and leave the others there. - cback(miner.client.socket.localPort == proxyPort) + cback(proxyPorts.indexOf(miner.client.socket.localPort.toString()) !== -1) }, function (clients) { newPool.attachMiners(clients); } ); - proxySwitch[algo].currentPool = newCoin; + proxySwitch[switchName].currentPool = newCoin; - var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host) - redisClient.on('ready', function(){ - redisClient.hset('proxyState', algo, newCoin, function(error, obj) { - if (error) { - logger.error(logSystem, logComponent, logSubCat, 'Redis error writing proxy config: ' + JSON.stringify(err)) - } - else { - logger.debug(logSystem, logComponent, logSubCat, 'Last proxy state saved to redis for ' + algo); - } - }); + redisClient.hset('proxyState', algo, newCoin, function(error, obj) { + if (error) { + logger.error(logSystem, logComponent, logSubCat, 'Redis error writing proxy config: ' + JSON.stringify(err)) + } + else { + logger.debug(logSystem, logComponent, logSubCat, 'Last proxy state saved to redis for ' + algo); + } }); + } break; } @@ -119,7 +132,7 @@ module.exports = function(logger){ if (shareProcessing && shareProcessing.mpos && shareProcessing.mpos.enabled){ var mposCompat = new MposCompatibility(logger, poolOptions); - handlers.auth = function(workerName, password, authCallback){ + handlers.auth = function(port, workerName, password, authCallback){ mposCompat.handleAuth(workerName, password, authCallback); }; @@ -137,10 +150,30 @@ module.exports = function(logger){ var shareProcessor = new ShareProcessor(logger, poolOptions); - handlers.auth = function(workerName, password, authCallback){ + handlers.auth = function(port, workerName, password, authCallback){ if (shareProcessing.internal.validateWorkerAddress !== true) authCallback(true); else { + port = port.toString(); + if (portalConfig.switching) { + for (var switchName in portalConfig.switching) { + if (portalConfig.switching[switchName].enabled && Object.keys(portalConfig.switching[switchName].ports).indexOf(port) !== -1) { + if (workerName.length === 40) { + try { + new Buffer(workerName, 'hex'); + authCallback(true); + } + catch (e) { + authCallback(false); + } + } + else + authCallback(false); + return; + } + } + } + pool.daemon.cmd('validateaddress', [workerName], function(results){ var isValid = results.filter(function(r){return r.response.isvalid}).length > 0; authCallback(isValid); @@ -153,8 +186,8 @@ module.exports = function(logger){ }; } - var authorizeFN = function (ip, workerName, password, callback) { - handlers.auth(workerName, password, function(authorized){ + var authorizeFN = function (ip, port, workerName, password, callback) { + handlers.auth(port, workerName, password, function(authorized){ var authString = authorized ? 'Authorized' : 'Unauthorized '; @@ -202,9 +235,9 @@ module.exports = function(logger){ }); - if (typeof(portalConfig.proxy) !== 'undefined') { + if (portalConfig.switching) { - var logSystem = 'Proxy'; + var logSystem = 'Switching'; var logComponent = 'Setup'; var logSubCat = 'Thread ' + (parseInt(forkId) + 1); @@ -215,73 +248,93 @@ module.exports = function(logger){ // on the last pool it was using when reloaded or restarted // logger.debug(logSystem, logComponent, logSubCat, 'Loading last proxy state from redis'); - var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host); - redisClient.on('ready', function(){ - redisClient.hgetall("proxyState", function(error, obj) { - if (error || obj == null) { - //logger.debug(logSystem, logComponent, logSubCat, 'No last proxy state found in redis'); - } - else { - proxyState = obj; - logger.debug(logSystem, logComponent, logSubCat, 'Last proxy state loaded from redis'); - } - // - // Setup proxySwitch object to control proxy operations from configuration and any restored - // state. Each algorithm has a listening port, current coin name, and an active pool to - // which traffic is directed when activated in the config. - // - // In addition, the proxy config also takes diff and varDiff parmeters the override the - // defaults for the standard config of the coin. - // - Object.keys(portalConfig.proxy).forEach(function(algorithm) { - if (portalConfig.proxy[algorithm].enabled === true) { - var initalPool = proxyState.hasOwnProperty(algorithm) ? proxyState[algorithm] : _this.getFirstPoolForAlgorithm(algorithm); - proxySwitch[algorithm] = { - port: portalConfig.proxy[algorithm].port, - currentPool: initalPool, - proxy: {} - }; - - // Copy diff and vardiff configuation into pools that match our algorithm so the stratum server can pick them up - // - // Note: This seems a bit wonky and brittle - better if proxy just used the diff config of the port it was - // routed into instead. - // - if (portalConfig.proxy[algorithm].hasOwnProperty('varDiff')) { - proxySwitch[algorithm].varDiff = new Stratum.varDiff(proxySwitch[algorithm].port, portalConfig.proxy[algorithm].varDiff); - proxySwitch[algorithm].diff = portalConfig.proxy[algorithm].diff; - } - Object.keys(pools).forEach(function (coinName) { - var a = poolConfigs[coinName].coin.algorithm; - var p = pools[coinName]; - if (a === algorithm) { - p.setVarDiff(proxySwitch[algorithm].port, proxySwitch[algorithm].varDiff); + /*redisClient.on('error', function(err){ + logger.debug(logSystem, logComponent, logSubCat, 'Pool configuration failed: ' + err); + });*/ + + redisClient.hgetall("proxyState", function(error, obj) { + if (error || obj == null) { + //logger.debug(logSystem, logComponent, logSubCat, 'No last proxy state found in redis'); + } + else { + proxyState = obj; + logger.debug(logSystem, logComponent, logSubCat, 'Last proxy state loaded from redis'); + } + + // + // Setup proxySwitch object to control proxy operations from configuration and any restored + // state. Each algorithm has a listening port, current coin name, and an active pool to + // which traffic is directed when activated in the config. + // + // In addition, the proxy config also takes diff and varDiff parmeters the override the + // defaults for the standard config of the coin. + // + Object.keys(portalConfig.switching).forEach(function(switchName) { + + var algorithm = portalConfig.switching[switchName].algorithm; + + if (portalConfig.switching[switchName].enabled === true) { + var initalPool = proxyState.hasOwnProperty(algorithm) ? proxyState[algorithm] : _this.getFirstPoolForAlgorithm(algorithm); + proxySwitch[switchName] = { + algorithm: algorithm, + ports: portalConfig.switching[switchName].ports, + currentPool: initalPool, + servers: [] + }; + + + // Copy diff and vardiff configuation into pools that match our algorithm so the stratum server can pick them up + // + // Note: This seems a bit wonky and brittle - better if proxy just used the diff config of the port it was + // routed into instead. + // + /*if (portalConfig.proxy[algorithm].hasOwnProperty('varDiff')) { + proxySwitch[algorithm].varDiff = new Stratum.varDiff(proxySwitch[algorithm].port, portalConfig.proxy[algorithm].varDiff); + proxySwitch[algorithm].diff = portalConfig.proxy[algorithm].diff; + }*/ + + + + Object.keys(pools).forEach(function (coinName) { + var p = pools[coinName]; + if (poolConfigs[coinName].coin.algorithm === algorithm) { + for (var port in portalConfig.switching[switchName].ports) { + if (portalConfig.switching[switchName].ports[port].vardiff) + p.setVarDiff(port, portalConfig.switching[switchName].ports[port].vardiff); } - }); + } + }); - proxySwitch[algorithm].proxy = net.createServer(function(socket) { - var currentPool = proxySwitch[algorithm].currentPool; - var logSubCat = 'Thread ' + (parseInt(forkId) + 1); - logger.debug(logSystem, 'Connect', logSubCat, 'Proxy connect from ' + socket.remoteAddress + ' on ' + proxySwitch[algorithm].port - + ' routing to ' + currentPool); + Object.keys(proxySwitch[switchName].ports).forEach(function(port){ + var f = net.createServer(function(socket) { + var currentPool = proxySwitch[switchName].currentPool; + + logger.debug(logSystem, 'Connect', logSubCat, 'Connection to ' + + switchName + ' from ' + + socket.remoteAddress + ' on ' + + port + ' routing to ' + currentPool); + pools[currentPool].getStratumServer().handleNewClient(socket); - }).listen(parseInt(proxySwitch[algorithm].port), function() { - logger.debug(logSystem, logComponent, logSubCat, 'Proxy listening for ' + algorithm + ' on port ' + proxySwitch[algorithm].port - + ' into ' + proxySwitch[algorithm].currentPool); + }).listen(parseInt(port), function() { + logger.debug(logSystem, logComponent, logSubCat, 'Switching "' + switchName + + '" listening for ' + algorithm + + ' on port ' + port + + ' into ' + proxySwitch[switchName].currentPool); }); - } - else { - //logger.debug(logSystem, logComponent, logSubCat, 'Proxy pool for ' + algorithm + ' disabled.'); - } - }); + proxySwitch[switchName].servers.push(f); + }); + + + } + else { + //logger.debug(logSystem, logComponent, logSubCat, 'Proxy pool for ' + algorithm + ' disabled.'); + } }); - }).on('error', function(err){ - logger.debug(logSystem, logComponent, logSubCat, 'Pool configuration failed: ' + err); }); } diff --git a/libs/stats.js b/libs/stats.js index 1f3c3d6..527e19d 100644 --- a/libs/stats.js +++ b/libs/stats.js @@ -148,7 +148,11 @@ module.exports = function(logger, portalConfig, poolConfigs){ symbol: poolConfigs[coinName].coin.symbol.toUpperCase(), algorithm: poolConfigs[coinName].coin.algorithm, hashrates: replies[i + 1], - poolStats: replies[i + 2] != null ? replies[i + 2] : { validShares: 0, validBlocks: 0, invalidShares: 0 }, + poolStats: { + validShares: replies[i + 2] ? (replies[i + 2].validShares || 0) : 0, + validBlocks: replies[i + 2] ? (replies[i + 2].validBlocks || 0) : 0, + invalidShares: replies[i + 2] ? (replies[i + 2].invalidShares || 0) : 0 + }, blocks: { pending: replies[i + 3], confirmed: replies[i + 4], diff --git a/scripts/blockNotify.js b/scripts/blockNotify.js deleted file mode 100644 index 42a0f8c..0000000 --- a/scripts/blockNotify.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - This script should be hooked to the coin daemon as follow: - litecoind -blocknotify="node /path/to/this/script/blockNotify.js 127.0.0.1:8117 password litecoin %s" - The above will send tell litecoin to launch this script with those parameters every time a block is found. - This script will then send the blockhash along with other information to a listening tcp socket - */ - -var net = require('net'); -var config = process.argv[2]; -var parts = config.split(':'); -var host = parts[0]; -var port = parts[1]; -var password = process.argv[3]; -var coin = process.argv[4]; -var blockHash = process.argv[5]; - -var client = net.connect(port, host, function () { - console.log('client connected'); - client.write(JSON.stringify({ - password: password, - coin: coin, - hash: blockHash - }) + '\n'); -}); - -client.on('data', function (data) { - console.log(data.toString()); - //client.end(); -}); - -client.on('end', function () { - console.log('client disconnected'); - //process.exit(); -}); \ No newline at end of file diff --git a/scripts/blocknotify.c b/scripts/blocknotify.c index 78c9db7..a119096 100644 --- a/scripts/blocknotify.c +++ b/scripts/blocknotify.c @@ -16,76 +16,69 @@ Simple lightweight & fast - a more efficient block notify script in pure C. (may also work as coin switch) -Platforms : Linux,BSD,Solaris (mostly OS independent) +Platforms : Linux, BSD, Solaris (mostly OS independent) Build with: gcc blocknotify.c -o blocknotify -Usage in daemon coin.conf - blocknotify="/bin/blocknotify 127.0.0.1:8117 mySuperSecurePassword dogecoin %s" +Example usage in daemon coin.conf using default NOMP CLI port of 17117 + blocknotify="/bin/blocknotify 127.0.0.1:17117 dogecoin %s" -*NOTE* If you use "localhost" as hostname you may get a "13" error (socket / connect / send may consider "localhost" as a broadcast address) -// {"password":"notepas","coin":"Xcoin","hash":"d2191a8b644c9cd903439edf1d89ee060e196b3e116e0d48a3f11e5e3987a03b"} -// simplest connect + send json string to server -# $Id: blocknotify.c,v 0.1 2014/04/07 22:38:09 sysman Exp $ */ int main(int argc, char **argv) { - int sockfd,n; - struct sockaddr_in servaddr,cliaddr; - char sendline[1000]; - char recvline[1000]; - char host[200]; - char *p,*arg,*errptr; - int port; + int sockfd,n; + struct sockaddr_in servaddr, cliaddr; + char sendline[1000]; + char recvline[1000]; + char host[200]; + char *p, *arg, *errptr; + int port; - if (argc < 4) - { - // print help - printf("NOMP pool block notify\n usage: \n"); - exit(1); - } + if (argc < 3) + { + // print help + printf("NOMP pool block notify\n usage: \n"); + exit(1); + } - strncpy(host,argv[1],(sizeof(host)-1)); - p=host; + strncpy(host, argv[1], (sizeof(host)-1)); + p = host; - if ( (arg=strchr(p,':')) ) - { - *arg='\0'; + if ( (arg = strchr(p,':')) ) + { + *arg = '\0'; - errno=0; // reset errno - port=strtol(++arg,&errptr,10); + errno = 0; // reset errno + port = strtol(++arg, &errptr, 10); - if ( (errno != 0) || (errptr == arg) ) { fprintf(stderr, "port number fail [%s]\n",errptr); } - // if(strlen(arg) > (errptr-arg) ) also fail, but we ignore it for now - // printf("host %s:%d\n",host,port); - } + if ( (errno != 0) || (errptr == arg) ) + { + fprintf(stderr, "port number fail [%s]\n", errptr); + } - // printf("pass: %s coin: %s block:[%s]\n",argv[2],argv[3],argv[4]); - snprintf(sendline,sizeof(sendline)-1, - "{\"password\":\"%s\",\"coin\":\"%s\",\"hash\":\"%s\"}\n", - argv[2], argv[3], argv[4]); + } - // printf("sendline:[%s]",sendline); + snprintf(sendline, sizeof(sendline) - 1, "{\"command\":\"blocknotify\",\"params\":[\"%s\",\"%s\"]}\n", argv[2], argv[3]); - sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); - bzero(&servaddr,sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr=inet_addr(host); - servaddr.sin_port=htons(port); - connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(host); + servaddr.sin_port = htons(port); + connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); - int result = send(sockfd,sendline,strlen(sendline),0); - close(sockfd); + int result = send(sockfd, sendline, strlen(sendline), 0); + close(sockfd); - if(result == -1) { - printf("Error sending: %i\n",errno); + if(result == -1) { + printf("Error sending: %i\n", errno); exit(-1); - } - exit(0); + } + exit(0); } diff --git a/scripts/cli.js b/scripts/cli.js new file mode 100644 index 0000000..013678c --- /dev/null +++ b/scripts/cli.js @@ -0,0 +1,38 @@ +var net = require('net'); + +var defaultPort = 17117; +var defaultHost = '127.0.0.1'; + +var args = process.argv.slice(2); +var params = []; +var options = {}; + +for(var i = 0; i < args.length; i++){ + if (args[i].indexOf('-') === 0 && args[i].indexOf('=') !== -1){ + var s = args[i].substr(1).split('='); + options[s[0]] = s[1]; + } + else + params.push(args[i]); +} + +var command = params.shift(); + + + +var client = net.connect(options.port || defaultPort, options.host || defaultHost, function () { + client.write(JSON.stringify({ + command: command, + params: params, + options: options + }) + '\n'); +}).on('error', function(error){ + if (error.code === 'ECONNREFUSED') + console.log('Could not connect to NOMP instance at ' + defaultHost + ':' + defaultPort); + else + console.log('Socket error ' + JSON.stringify(error)); +}).on('data', function(data) { + console.log(data.toString()); +}).on('close', function () { + +}); \ No newline at end of file diff --git a/scripts/coinSwitch.js b/scripts/coinSwitch.js deleted file mode 100644 index acf6ac1..0000000 --- a/scripts/coinSwitch.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - This script demonstrates sending a coin switch request and can be invoked from the command line - with: - - "node coinSwitch.js 127.0.0.1:8118 password %s" - - where <%s> is the name of the coin proxy miners will be switched onto. - - If the coin name is not configured, disabled or matches the existing proxy setting, no action - will be taken by NOMP on receipt of the message. - */ - -var net = require('net'); -var config = process.argv[2]; -var parts = config.split(':'); -var host = parts[0]; -var port = parts[1]; -var password = process.argv[3]; -var coin = process.argv[4]; - -var client = net.connect(port, host, function () { - console.log('client connected'); - client.write(JSON.stringify({ - password: password, - coin: coin - }) + '\n'); -}); - -client.on('data', function (data) { - console.log(data.toString()); - //client.end(); -}); - -client.on('end', function () { - console.log('client disconnected'); - //process.exit(); -}); diff --git a/website/pages/tbs.html b/website/pages/tbs.html index ccc1b3b..12ce72b 100644 --- a/website/pages/tbs.html +++ b/website/pages/tbs.html @@ -1,56 +1,59 @@ - + + + + + + + + + + + + + + + {{ for(var pool in it.stats.pools) { }} + + + + + + + + + + {{ } }} +
PoolAlgoWorkersValid SharesInvalid SharesBlocksHashrate
{{=it.stats.pools[pool].name}}{{=it.stats.pools[pool].algorithm}}{{=Object.keys(it.stats.pools[pool].workers).length}}{{=it.stats.pools[pool].poolStats.validShares}}{{=it.stats.pools[pool].poolStats.invalidShares}}{{=it.stats.pools[pool].poolStats.validBlocks}}{{=it.stats.pools[pool].hashrateString}}