diff --git a/README.md b/README.md index baa047b..d333499 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,34 @@ #### Node Open Mining Portal This portal is an extremely efficient, highly scalable, all-in-one, easy to setup cryptocurrency mining pool written -entirely in Node.js. It contains a stratum poolserver, reward/payment/share processor, and a (*not yet completed*) -front-end website. +entirely in Node.js. It contains a stratum poolserver; reward/payment/share processor; and a (*not yet completed*) +responsive user-friendly front-end website featuring mining instructions, in-depth live statistics, and an admin center. -#### Features +#### Table of Contents +* [Features](#features) + * [Attack Mitigation](##attack-mitigation) + * [Security](#security) + * [Planned Features](#planned-features) +* [Community Support](#community--support) +* [Usage](#usage) + * [Requirements](#requirements) + * [Setting Up Coin Daemon](#0-setting-up-coin-daemon) + * [Downloading & Installing](#1-downloading--installing) + * [Configuration](#2-configuration) + * [Portal Config](#portal-config) + * [Coin Config](#coin-config) + * [Pool Config](#pool-config) + * [Setting Up Blocknotify](#optional-recommended-setting-up-blocknotify) + * [Starting the Portal](#3-start-the-portal) + * [Upgrading NOMP](#upgrading-nomp) +* [Donations](#donations) +* [Credits](#credits) +* [License](#license) + + + + +### Features * For the pool server it uses the highly efficient [node-stratum-pool](//github.com/zone117x/node-stratum-pool) module which supports vardiff, POW & POS, transaction messages, anti-DDoS, IP banning, [several hashing algorithms](//github.com/zone117x/node-stratum-pool#hashing-algorithms-supported). @@ -64,7 +88,7 @@ allow your own pool to connect upstream to a larger pool server. It will request redistribute the work to our own connected miners. -#### Community / Support +### Community / Support IRC * Support / general discussion join #nomp: https://webchat.freenode.net/?channels=#nomp * Development discussion join #nomp-dev: https://webchat.freenode.net/?channels=#nomp-dev @@ -80,8 +104,11 @@ If your pool uses NOMP let us know and we will list your website here. * http://chunkypools.com * http://clevermining.com * http://rapidhash.net +* http://suchpool.pw * http://hashfaster.com +* http://miningpoolhub.com * http://kryptochaos.com +* http://pool.uberpools.org Usage @@ -156,12 +183,7 @@ Explanation for each field: /* How many seconds to hold onto historical stats. Currently set to 24 hours. */ "historicalRetention": 43200, /* How many seconds worth of shares should be gathered to generate hashrate. */ - "hashrateWindow": 300, - /* Redis instance of where to store historical stats. */ - "redis": { - "host": "localhost", - "port": 6379 - } + "hashrateWindow": 300 }, /* Not done yet. */ "adminCenter": { @@ -170,6 +192,13 @@ Explanation for each field: } }, + /* Redis instance of where to store global portal data such as historical stats, proxy states, + ect.. */ + "redis": { + "host": "127.0.0.1", + "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 @@ -290,6 +319,12 @@ Description of options: due to mining apps using incorrect max diffs and this pool using correct max diffs. */ "shareVariancePercent": 10, + /* Enable for client IP addresses to be detected when using a load balancer with TCP proxy + protocol enabled, such as HAProxy with 'send-proxy' param: + http://haproxy.1wt.eu/download/1.5/doc/configuration.txt */ + "tcpProxyProtocol": false, + + /* This determines what to do with submitted shares (and stratum worker authentication). You have two options: 1) Enable internal and disable mpos = this portal to handle all share payments. @@ -338,7 +373,7 @@ Description of options: configured 'address' that receives the block rewards, otherwise the daemon will not be able to confirm blocks or send out payments. */ "daemon": { - "host": "localhost", + "host": "127.0.0.1", "port": 19332, "user": "litecoinrpc", "password": "testnet" @@ -346,7 +381,7 @@ Description of options: /* Redis database used for storing share and block submission data. */ "redis": { - "host": "localhost", + "host": "127.0.0.1", "port": 6379 } }, @@ -355,7 +390,7 @@ Description of options: also want to use the "emitInvalidBlockHashes" option below if you require it. */ "mpos": { "enabled": false, - "host": "localhost", //MySQL db host + "host": "127.0.0.1", //MySQL db host "port": 3306, //MySQL db port "user": "me", //MySQL db user "password": "mypass", //MySQL db password @@ -368,8 +403,10 @@ Description of options: } }, - /* If a worker is submitting a high threshold of invalid shares we can temporarily ban them - to reduce system/network load. Also useful to fight against flooding attacks. */ + /* If a worker is submitting a high threshold of invalid shares we can temporarily ban their IP + to reduce system/network load. Also useful to fight against flooding attacks. The worker's + If running behind something like HAProxy be sure to enable the TCP Proxy Protocol config, + otherwise you'll end up banning your own IP address (and therefore all workers). */ "banning": { "enabled": true, "time": 600, //How many seconds to ban worker for @@ -404,13 +441,13 @@ Description of options: drops out-of-sync or offline. */ "daemons": [ { //Main daemon instance - "host": "localhost", + "host": "127.0.0.1", "port": 19332, "user": "litecoinrpc", "password": "testnet" }, { //Backup daemon instance - "host": "localhost", + "host": "127.0.0.1", "port": 19344, "user": "litecoinrpc", "password": "testnet" @@ -423,7 +460,7 @@ Description of options: intensive than blocknotify script). However its still under development (not yet working). */ "p2p": { "enabled": false, - "host": "localhost", + "host": "127.0.0.1", "port": 19333, /* Magic value is different for main/testnet and for each coin. It is found in the daemon @@ -455,7 +492,7 @@ node [path to scripts/blockNotify.js] [listener host]:[listener port] [listener ``` Example: inside `dogecoin.conf` add the line ``` -blocknotify="node scripts/blockNotify.js localhost:8117 mySuperSecurePassword dogecoin %s" +blocknotify="node scripts/blockNotify.js 127.0.0.1:8117 mySuperSecurePassword dogecoin %s" ``` Alternatively, you can use a more efficient block notify script written in pure C. Build and usage instructions @@ -506,7 +543,8 @@ Credits * [TheSeven](//github.com/TheSeven) - answering an absurd amount of my questions and being a very helpful gentleman * [UdjinM6](//github.com/UdjinM6) - helped implement fee withdrawal in payment processing * [Alex Petrov / sysmanalex](https://github.com/sysmanalex) - contributed the pure C block notify script -* Those that contributed to [node-stratum-pool](//github.com/zone117x/node-stratum-pool) +* [svirusxxx](//github.com/svirusxxx) - sponsored development of MPOS mode +* Those that contributed to [node-stratum-pool](//github.com/zone117x/node-stratum-pool#credits) License diff --git a/config_example.json b/config_example.json index 8cd417c..55e5e9b 100644 --- a/config_example.json +++ b/config_example.json @@ -13,11 +13,7 @@ "stats": { "updateInterval": 60, "historicalRetention": 43200, - "hashrateWindow": 300, - "redis": { - "host": "localhost", - "port": 6379 - } + "hashrateWindow": 300 }, "adminCenter": { "enabled": true, @@ -25,6 +21,11 @@ } }, + "redis": { + "host": "127.0.0.1", + "port": 6379 + }, + "blockNotifyListener": { "enabled": false, "port": 8117, diff --git a/init.js b/init.js index 0daf3fd..8044bb0 100644 --- a/init.js +++ b/init.js @@ -8,7 +8,6 @@ var PoolLogger = require('./libs/logUtil.js'); var BlocknotifyListener = require('./libs/blocknotifyListener.js'); var CoinswitchListener = require('./libs/coinswitchListener.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'); var Website = require('./libs/website.js'); @@ -139,6 +138,7 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ return portalConfig.clustering.forks; })(); + var poolWorkers = {}; var createPoolWorker = function(forkId){ var worker = cluster.fork({ @@ -147,11 +147,24 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ pools: serializedConfigs, portalConfig: JSON.stringify(portalConfig) }); + worker.forkId = forkId; + worker.type = 'pool'; + poolWorkers[forkId] = worker; worker.on('exit', function(code, signal){ logger.error('Master', 'PoolSpanwer', 'Fork ' + forkId + ' died, spawning replacement worker...'); 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; + } }); }; @@ -168,12 +181,6 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ }; -var startWorkerListener = function(poolConfigs){ - var workerListener = new WorkerListener(logger, poolConfigs); - workerListener.init(); -}; - - var startBlockListener = function(portalConfig){ //block notify options //setup block notify here and use IPC to tell appropriate pools @@ -301,8 +308,6 @@ var startWebsite = function(portalConfig, poolConfigs){ startRedisBlockListener(portalConfig); - startWorkerListener(poolConfigs); - startWebsite(portalConfig, poolConfigs); })(); diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index 26b30a2..80bc0d8 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -41,7 +41,7 @@ module.exports = function(logger, poolConfig){ connection.query( 'SELECT password FROM pool_worker WHERE username = LOWER(?)', - [workerName], + [workerName.toLowerCase()], function(err, result){ if (err){ logger.error(logIdentify, logComponent, 'Database error when authenticating worker: ' + diff --git a/libs/poolWorker.js b/libs/poolWorker.js index 71da314..c8b6680 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -22,6 +22,13 @@ module.exports = function(logger){ process.on('message', function(message) { switch(message.type){ + case 'banIP': + for (var p in pools){ + if (pools[p].stratumServer) + pools[p].stratumServer.addBannedIP(message.ip); + } + break; + case 'blocknotify': var messageCoin = message.coin.toLowerCase(); @@ -75,7 +82,7 @@ module.exports = function(logger){ ); proxySwitch[algo].currentPool = newCoin; - var redisClient = redis.createClient(6379, "localhost") + var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host) redisClient.on('ready', function(){ redisClient.hset('proxyState', algo, newCoin, function(error, obj) { if (error) { @@ -186,6 +193,8 @@ module.exports = function(logger){ handlers.diff(workerName, diff); }).on('log', function(severity, text) { logger[severity](logSystem, logComponent, logSubCat, text); + }).on('banIP', function(ip, worker){ + process.send({type: 'banIP', ip: ip}); }); pool.start(); @@ -206,7 +215,7 @@ 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(6379, "localhost") + var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host) redisClient.on('ready', function(){ redisClient.hgetall("proxyState", function(error, obj) { if (error || obj == null) { diff --git a/libs/stats.js b/libs/stats.js index 62b7f00..0893506 100644 --- a/libs/stats.js +++ b/libs/stats.js @@ -59,7 +59,7 @@ module.exports = function(logger, portalConfig, poolConfigs){ function setupStatsRedis(){ - redisStats = redis.createClient(portalConfig.website.stats.redis.port, portalConfig.website.stats.redis.host); + redisStats = redis.createClient(portalConfig.redis.port, portalConfig.redis.host); redisStats.on('error', function(err){ logger.error(logSystem, 'Historics', 'Redis for stats had an error ' + JSON.stringify(err)); }); diff --git a/libs/workerListener.js b/libs/workerListener.js deleted file mode 100644 index 6bb669b..0000000 --- a/libs/workerListener.js +++ /dev/null @@ -1,26 +0,0 @@ -var events = require('events'); -var cluster = require('cluster'); - -var MposCompatibility = require('./mposCompatibility.js'); -var ShareProcessor = require('./shareProcessor.js'); - - -var processor = module.exports = function processor(logger, poolConfigs){ - - var _this = this; - - - this.init = function(){ - - Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].on('message', function(data){ - switch(data.type){ - - } - }); - }); - } -}; - - -processor.prototype.__proto__ = events.EventEmitter.prototype; diff --git a/pool_configs/litecoin_example.json b/pool_configs/litecoin_example.json index a753c31..82b243e 100644 --- a/pool_configs/litecoin_example.json +++ b/pool_configs/litecoin_example.json @@ -10,6 +10,8 @@ "emitInvalidBlockHashes": false, "shareVariancePercent": 15, + "tcpProxyProtocol": false, + "shareProcessing": { "internal": { "enabled": true, @@ -22,19 +24,19 @@ "feeReceiveAddress": "mppaGeNaSbG1Q7S6V3gL5uJztMhucgL9Vh", "feeWithdrawalThreshold": 5, "daemon": { - "host": "localhost", + "host": "127.0.0.1", "port": 19332, "user": "litecoinrpc", "password": "testnet" }, "redis": { - "host": "localhost", + "host": "127.0.0.1", "port": 6379 } }, "mpos": { "enabled": false, - "host": "localhost", + "host": "127.0.0.1", "port": 3306, "user": "me", "password": "mypass", @@ -72,13 +74,13 @@ "daemons": [ { - "host": "localhost", + "host": "127.0.0.1", "port": 19332, "user": "litecoinrpc", "password": "testnet" }, { - "host": "localhost", + "host": "127.0.0.1", "port": 19344, "user": "litecoinrpc", "password": "testnet" @@ -87,7 +89,7 @@ "p2p": { "enabled": false, - "host": "localhost", + "host": "127.0.0.1", "port": 19333, "protocolVersion": 70002, "magic": "fcc1b7dc" diff --git a/scripts/blockNotify.js b/scripts/blockNotify.js index 67ed235..42a0f8c 100644 --- a/scripts/blockNotify.js +++ b/scripts/blockNotify.js @@ -1,6 +1,6 @@ /* This script should be hooked to the coin daemon as follow: - litecoind -blocknotify="node /path/to/this/script/blockNotify.js localhost:8117 password litecoin %s" + 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 */ diff --git a/scripts/coinSwitch.js b/scripts/coinSwitch.js index 18dc1c8..acf6ac1 100644 --- a/scripts/coinSwitch.js +++ b/scripts/coinSwitch.js @@ -2,7 +2,7 @@ This script demonstrates sending a coin switch request and can be invoked from the command line with: - "node coinSwitch.js localhost:8118 password %s" + "node coinSwitch.js 127.0.0.1:8118 password %s" where <%s> is the name of the coin proxy miners will be switched onto.