This commit is contained in:
Jerry Brady 2014-04-16 22:31:19 +00:00
commit 9213b6bf96
13 changed files with 141 additions and 82 deletions

105
README.md
View File

@ -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).
@ -13,6 +37,7 @@ supports vardiff, POW & POS, transaction messages, anti-DDoS, IP banning, [sever
* The portal has an [MPOS](//github.com/MPOS/php-mpos) compatibility mode so that the it can
function as a drop-in-replacement for [python-stratum-mining](//github.com/Crypto-Expert/stratum-mining). This
mode can be enabled in the configuration and will insert shares into a MySQL database in the format which MPOS expects.
For a direct tutorial see the wiki page [Setting up NOMP for MPOS usage](//github.com/zone117x/node-open-mining-portal/wiki/Setting-up-NOMP-for-MPOS-usage).
* Multi-pool ability - this software was built from the ground up to run with multiple coins simultaneously (which can
have different properties and hashing algorithms). It can be used to create a pool for a single coin or for multiple
@ -64,7 +89,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 +105,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
@ -93,6 +121,8 @@ Usage
* [Node.js](http://nodejs.org/) v0.10+ ([follow these installation instructions](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager))
* [Redis](http://redis.io/) key-value store v2.6+ ([follow these instructions](http://redis.io/topics/quickstart))
##### Seriously
Those are legitimate requirements. If you use old versions of Node.js or Redis that may come with your system package manager then you will have problems. Follow the linked instructions to get the last stable versions.
#### 0) Setting up coin daemon
Follow the build/install instructions for your coin daemon. Your coin.conf file should end up looking something like this:
@ -156,12 +186,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 +195,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
@ -291,6 +323,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.
@ -339,7 +377,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"
@ -347,7 +385,7 @@ Description of options:
/* Redis database used for storing share and block submission data. */
"redis": {
"host": "localhost",
"host": "127.0.0.1",
"port": 6379
}
},
@ -356,7 +394,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
@ -369,8 +407,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. If running
behind something like HAProxy be sure to enable 'tcpProxyProtocol', 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
@ -405,13 +445,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"
@ -419,23 +459,29 @@ Description of options:
],
/* This allows the pool to connect to the daemon as a node peer to recieve block updates.
/* This allows the pool to connect to the daemon as a node peer to receive block updates.
It may be the most efficient way to get block updates (faster than polling, less
intensive than blocknotify script). However its still under development (not yet working). */
intensive than blocknotify script). It requires additional setup: the 'magic' field must
be exact (extracted from the coin source code). */
"p2p": {
"enabled": false,
"host": "localhost",
/* Host for daemon */
"host": "127.0.0.1",
/* Port configured for daemon (this is the actual peer port not RPC port) */
"port": 19333,
/* If your coin daemon is new enough (i.e. not a shitcoin) then it will support a p2p
feature that prevents the daemon from spamming our peer node with unnecessary
transaction data. Assume its supported but if you have problems try disabling it. */
"disableTransactions": true,
/* Magic value is different for main/testnet and for each coin. It is found in the daemon
source code as the pchMessageStart variable.
For example, litecoin mainnet magic: http://git.io/Bi8YFw
And for litecoin testnet magic: http://git.io/NXBYJA
*/
"magic": "fcc1b7dc",
//Found in src as the PROTOCOL_VERSION variable, for example: http://git.io/KjuCrw
"protocolVersion": 70002,
And for litecoin testnet magic: http://git.io/NXBYJA */
"magic": "fcc1b7dc"
}
}
@ -456,7 +502,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
@ -503,11 +549,14 @@ Credits
-------
* [Jerry Brady / mintyfresh68](https://github.com/bluecircle) - got coin-switching fully working and developed proxy-per-algo feature
* [Tony Dobbs](http://anthonydobbs.com) - designs for front-end and created the NOMP logo
* [LucasJones(//github.com/LucasJones) - getting p2p block notify script working
* [vekexasia](//github.com/vekexasia) - co-developer & great tester
* [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
* [icecube45](//github.com/icecube45) - helping out with the repo wiki
* Those that contributed to [node-stratum-pool](//github.com/zone117x/node-stratum-pool#credits)
License

7
coins/copperlark.json Normal file
View File

@ -0,0 +1,7 @@
{
"name": "Copperlark",
"symbol": "CLR",
"algorithm": "keccak",
"normalHashing": true,
"diffShift": 32
}

5
coins/cryptometh.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "Cryptometh",
"symbol": "METH",
"algorithm": "keccak"
}

7
coins/ecoin.json Normal file
View File

@ -0,0 +1,7 @@
{
"name": "Ecoin",
"symbol": "ECN",
"algorithm": "keccak",
"normalHashing": true,
"diffShift": 32
}

View File

@ -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,

23
init.js
View File

@ -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');
@ -143,6 +142,7 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){
return portalConfig.clustering.forks;
})();
var poolWorkers = {};
var createPoolWorker = function(forkId){
var worker = cluster.fork({
@ -151,11 +151,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;
}
});
};
@ -172,12 +185,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
@ -323,8 +330,6 @@ var startProfitSwitch = function(portalConfig, poolConfigs){
startRedisBlockListener(portalConfig);
startWorkerListener(poolConfigs);
startWebsite(portalConfig, poolConfigs);
startProfitSwitch(portalConfig, poolConfigs);

View File

@ -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: ' +

View File

@ -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();
@ -30,7 +37,7 @@ module.exports = function(logger){
})[0];
if (poolTarget)
pools[poolTarget].processBlockNotify(message.hash);
pools[poolTarget].processBlockNotify(message.hash, 'blocknotify script');
break;
@ -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) {

View File

@ -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));
});

View File

@ -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;

View File

@ -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,9 +89,9 @@
"p2p": {
"enabled": false,
"host": "localhost",
"host": "127.0.0.1",
"port": 19333,
"protocolVersion": 70002,
"disableTransactions": true,
"magic": "fcc1b7dc"
}
}

View File

@ -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
*/

View File

@ -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.