From f6610a914d62c8d318b3773e36bc36d27f4f78f4 Mon Sep 17 00:00:00 2001 From: icecube45 Date: Sat, 19 Apr 2014 12:32:47 -0700 Subject: [PATCH 01/35] Update quarkcoin.json --- coins/quarkcoin.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/coins/quarkcoin.json b/coins/quarkcoin.json index 797e7cd..983f340 100644 --- a/coins/quarkcoin.json +++ b/coins/quarkcoin.json @@ -1,5 +1,6 @@ { "name": "Quarkcoin", "symbol": "QRK", - "algorithm": "quark" -} \ No newline at end of file + "algorithm": "quark", + "mposDiffMultiplier": 256 +} From e58689956dd17dd616da9f6926464c00005e1574 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 20 Apr 2014 12:04:41 -0600 Subject: [PATCH 02/35] Changed stats to use algo multiplier --- README.md | 4 ++-- libs/poolWorker.js | 6 +++--- libs/stats.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8be38b8..c2e9cb2 100644 --- a/README.md +++ b/README.md @@ -302,8 +302,8 @@ Description of options: job broadcast. */ "txRefreshInterval": 20000, - /* Some miner software is bugged and will consider the pool offline if it doesn't receive - anything for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast + /* Some miner apps will consider the pool dead/offline if it doesn't receive anything new jobs + for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast in this many seconds unless we find a new job. Set to zero or remove to disable this. */ "jobRebroadcastTimeout": 55, diff --git a/libs/poolWorker.js b/libs/poolWorker.js index cb77079..adec236 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -117,7 +117,7 @@ module.exports = function(logger){ //Functions required for MPOS compatibility if (shareProcessing && shareProcessing.mpos && shareProcessing.mpos.enabled){ - var mposCompat = new MposCompatibility(logger, poolOptions) + var mposCompat = new MposCompatibility(logger, poolOptions); handlers.auth = function(workerName, password, authCallback){ mposCompat.handleAuth(workerName, password, authCallback); @@ -135,7 +135,7 @@ module.exports = function(logger){ //Functions required for internal payment processing else if (shareProcessing && shareProcessing.internal && shareProcessing.internal.enabled){ - var shareProcessor = new ShareProcessor(logger, poolOptions) + var shareProcessor = new ShareProcessor(logger, poolOptions); handlers.auth = function(workerName, password, authCallback){ if (shareProcessing.internal.validateWorkerAddress !== true) @@ -215,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(portalConfig.redis.port, portalConfig.redis.host) + 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 0893506..ade763b 100644 --- a/libs/stats.js +++ b/libs/stats.js @@ -191,7 +191,7 @@ module.exports = function(logger, portalConfig, poolConfigs){ else coinStats.workers[worker] = workerShares; }); - var shareMultiplier = algos[coinStats.algorithm].multiplier || 0; + var shareMultiplier = Math.pow(2, 32) / algos[coinStats.algorithm].multiplier; var hashratePre = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow; coinStats.hashrate = hashratePre | 0; coinStats.workerCount = Object.keys(coinStats.workers).length; From 59cfa8b1fe7fdc5bba53439f4d1396a9ae176a16 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 20 Apr 2014 12:10:19 -0600 Subject: [PATCH 03/35] Minor readme fix with anchor link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2e9cb2..d99804c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ responsive user-friendly front-end website featuring mining instructions, in-dep #### Table of Contents * [Features](#features) - * [Attack Mitigation](##attack-mitigation) + * [Attack Mitigation](#attack-mitigation) * [Security](#security) * [Planned Features](#planned-features) * [Community Support](#community--support) From a4a22784437b1dde397b9ddf95572e4a7ac954ec Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 20 Apr 2014 12:17:44 -0600 Subject: [PATCH 04/35] diff1 is a normal JS number now rather than a bignum --- libs/profitSwitch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/profitSwitch.js b/libs/profitSwitch.js index 7c1f449..83491a0 100644 --- a/libs/profitSwitch.js +++ b/libs/profitSwitch.js @@ -432,7 +432,7 @@ module.exports = function(logger){ // some shitcoins dont provide target, only bits, so we need to deal with both var target = response.target ? bignum(response.target, 16) : util.bignumFromBitsHex(response.bits); - coinStatus.difficulty = parseFloat((diff1.toNumber() / target.toNumber()).toFixed(9)); + coinStatus.difficulty = parseFloat((diff1 / target.toNumber()).toFixed(9)); logger.debug(logSystem, symbol, 'difficulty is ' + coinStatus.difficulty); coinStatus.reward = new Number(response.coinbasevalue / 100000000); From 9722c054f90b430ae0a090afbdde632a667a1821 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 20 Apr 2014 21:16:58 -0600 Subject: [PATCH 05/35] Added mintcoin --- coins/mintcoin.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 coins/mintcoin.json diff --git a/coins/mintcoin.json b/coins/mintcoin.json new file mode 100644 index 0000000..fb93cfb --- /dev/null +++ b/coins/mintcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Mintcoin", + "symbol": "MINT", + "algorithm": "scrypt" +} \ No newline at end of file From 882f779e204dfdad836ab17c08ae549474d18ddf Mon Sep 17 00:00:00 2001 From: k7 Date: Mon, 21 Apr 2014 16:09:37 +0800 Subject: [PATCH 06/35] auto create mpos worker --- libs/mposCompatibility.js | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index 659d290..94239b5 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -38,7 +38,7 @@ module.exports = function(logger, poolConfig){ connect(); this.handleAuth = function(workerName, password, authCallback){ - + connection.query( 'SELECT password FROM pool_worker WHERE username = LOWER(?)', [workerName.toLowerCase()], @@ -48,8 +48,39 @@ module.exports = function(logger, poolConfig){ JSON.stringify(err)); authCallback(false); } - else if (!result[0]) - authCallback(false); + else if (!result[0]){ + if(mposConfig.autoCreateWorker){ + var account = workerName.split('.')[0]; + connection.query( + 'SELECT username FROM accounts WHERE username = LOWER(?)', + [account.toLowerCase()], + function(err, result){ + if (err){ + logger.error(logIdentify, logComponent, 'Database error when authenticating account: ' + + JSON.stringify(err)); + authCallback(false); + }else if(!result[0]){ + authCallback(false); + }else{ + connection.query( + "INSERT INTO `pool_worker` (`id`, `account_id`, `username`, `password`, `difficulty`, `monitor`) VALUES (NULL, ?, ?, '123', '0', '0');", + [result[0].id,workerName.toLowerCase()], + function(err, result){ + if (err){ + logger.error(logIdentify, logComponent, 'Database error when insert worker: ' + + JSON.stringify(err)); + authCallback(false); + }else { + authCallback(true); + } + }) + } + } + ); + }else{ + authCallback(false); + } + } else if (mposConfig.stratumAuth === 'worker') authCallback(true); else if (result[0].password === password) From 75c091457b31b160fe541f5262271997b57ec51e Mon Sep 17 00:00:00 2001 From: k7 Date: Mon, 21 Apr 2014 23:58:50 +0800 Subject: [PATCH 07/35] fix auto create worker with right password --- libs/mposCompatibility.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index 94239b5..a82875d 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -63,8 +63,8 @@ module.exports = function(logger, poolConfig){ authCallback(false); }else{ connection.query( - "INSERT INTO `pool_worker` (`id`, `account_id`, `username`, `password`, `difficulty`, `monitor`) VALUES (NULL, ?, ?, '123', '0', '0');", - [result[0].id,workerName.toLowerCase()], + "INSERT INTO `pool_worker` (`id`, `account_id`, `username`, `password`, `difficulty`, `monitor`) VALUES (NULL, ?, ?, ?, '0', '0');", + [result[0].id,workerName.toLowerCase(),password], function(err, result){ if (err){ logger.error(logIdentify, logComponent, 'Database error when insert worker: ' + From 019865bc95061e2b51be527b2f0f974ed97aa7b2 Mon Sep 17 00:00:00 2001 From: k7 Date: Tue, 22 Apr 2014 00:19:15 +0800 Subject: [PATCH 08/35] fix auto create worker bug --- libs/mposCompatibility.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index a82875d..d94c8cd 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -52,7 +52,7 @@ module.exports = function(logger, poolConfig){ if(mposConfig.autoCreateWorker){ var account = workerName.split('.')[0]; connection.query( - 'SELECT username FROM accounts WHERE username = LOWER(?)', + 'SELECT id,username FROM accounts WHERE username = LOWER(?)', [account.toLowerCase()], function(err, result){ if (err){ @@ -63,7 +63,7 @@ module.exports = function(logger, poolConfig){ authCallback(false); }else{ connection.query( - "INSERT INTO `pool_worker` (`id`, `account_id`, `username`, `password`, `difficulty`, `monitor`) VALUES (NULL, ?, ?, ?, '0', '0');", + "INSERT INTO `pool_worker` (`account_id`, `username`, `password`) VALUES (?, ?, ?);", [result[0].id,workerName.toLowerCase(),password], function(err, result){ if (err){ From 39d02b9af9a1b9f9c436e284c3cadb59bf041dcf Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 21 Apr 2014 12:27:34 -0600 Subject: [PATCH 09/35] Added autoCreateWorker config to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d99804c..9fcb589 100644 --- a/README.md +++ b/README.md @@ -400,6 +400,10 @@ Description of options: "password": "mypass", //MySQL db password "database": "ltc", //MySQL db database name + /* Unregistered workers can automatically be registered (added to database) on stratum + worker authentication if this is true. */ + "autoCreateWorker": false, + /* For when miner's authenticate: set to "password" for both worker name and password to be checked for in the database, set to "worker" for only work name to be checked, or don't use this option (set to "none") for no auth checks */ From d7a26f67cdc6b9fa569dd0739f0ba00687aab78a Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 21 Apr 2014 12:40:19 -0600 Subject: [PATCH 10/35] minor readme update --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fcb589..3ba93c3 100644 --- a/README.md +++ b/README.md @@ -530,7 +530,8 @@ output from NOMP. #### Upgrading NOMP -When updating NOMP to the latest code its important to not only `git pull` the latest from this repo, but to also update the `node-statum-pool` module and any config files that may have been changed. +When updating NOMP to the latest code its important to not only `git pull` the latest from this repo, but to also update +the `node-statum-pool` and `node-multi-hashing` modules, and any config files that may have been changed. * Inside your NOMP directory (where the init.js script is) do `git pull` to get the latest NOMP code. * Remove the dependenices by deleting the `node_modules` directory with `rm -r node_modules`. * Run `npm update` to force updating/reinstalling of the dependencies. From 50a560d92f7523623c46c105620e162192f3365c Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 21 Apr 2014 12:44:35 -0600 Subject: [PATCH 11/35] readme markup fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ba93c3..acc53e2 100644 --- a/README.md +++ b/README.md @@ -554,7 +554,7 @@ 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 +* [LucasJones](//github.com/LucasJones) - got p2p block notify working and implemented additional hashing algos * [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 From a279fb486f19bfe97553ec9ab195ae47fae527db Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 23 Apr 2014 12:53:19 -0600 Subject: [PATCH 12/35] Probably fixed negative hashrate issue --- README.md | 1 + init.js | 4 ++-- libs/poolWorker.js | 4 ++-- libs/stats.js | 5 +++-- package.json | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index acc53e2..0a94830 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ If your pool uses NOMP let us know and we will list your website here. * http://suchpool.pw * http://hashfaster.com * http://miningpoolhub.com +* http://teamdoge.com * http://kryptochaos.com * http://pool.uberpools.org diff --git a/init.js b/init.js index a7b15b6..6ff3e74 100644 --- a/init.js +++ b/init.js @@ -296,8 +296,8 @@ var startWebsite = function(portalConfig, poolConfigs){ var startProfitSwitch = function(portalConfig, poolConfigs){ - if (!portalConfig.profitSwitch.enabled){ - logger.error('Master', 'Profit', 'Profit auto switching disabled'); + if (!portalConfig.profitSwitch || !portalConfig.profitSwitch.enabled){ + //logger.error('Master', 'Profit', 'Profit auto switching disabled'); return; } diff --git a/libs/poolWorker.js b/libs/poolWorker.js index 982c710..b1a5d47 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -219,7 +219,7 @@ module.exports = function(logger){ 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'); + //logger.debug(logSystem, logComponent, logSubCat, 'No last proxy state found in redis'); } else { proxyState = obj; @@ -276,7 +276,7 @@ module.exports = function(logger){ }); } else { - logger.debug(logSystem, logComponent, logSubCat, 'Proxy pool for ' + algorithm + ' disabled.'); + //logger.debug(logSystem, logComponent, logSubCat, 'Proxy pool for ' + algorithm + ' disabled.'); } }); }); diff --git a/libs/stats.js b/libs/stats.js index ade763b..1f3c3d6 100644 --- a/libs/stats.js +++ b/libs/stats.js @@ -191,9 +191,10 @@ module.exports = function(logger, portalConfig, poolConfigs){ else coinStats.workers[worker] = workerShares; }); + var shareMultiplier = Math.pow(2, 32) / algos[coinStats.algorithm].multiplier; - var hashratePre = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow; - coinStats.hashrate = hashratePre | 0; + coinStats.hashrate = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow; + coinStats.workerCount = Object.keys(coinStats.workers).length; portalStats.global.workers += coinStats.workerCount; diff --git a/package.json b/package.json index dc3e7c1..133d279 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-open-mining-portal", - "version": "0.0.3", + "version": "0.0.4", "description": "An extremely efficient, highly scalable, all-in-one, easy to setup cryptocurrency mining pool", "keywords": [ "stratum", From 432af28a78310ee13d6af2e20d2230492142b742 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 23 Apr 2014 23:59:53 -0600 Subject: [PATCH 13/35] Added reward address account detection in payment processing so addresses not under the default account of "" (empty string) will work. --- README.md | 1 + libs/paymentProcessor.js | 45 ++++++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0a94830..9595242 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ If your pool uses NOMP let us know and we will list your website here. * http://hashfaster.com * http://miningpoolhub.com * http://teamdoge.com +* http://miningwith.us * http://kryptochaos.com * http://pool.uberpools.org diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js index 3d3482a..77cf865 100644 --- a/libs/paymentProcessor.js +++ b/libs/paymentProcessor.js @@ -216,6 +216,8 @@ function SetupForPool(logger, poolOptions, setupFinished){ return ['gettransaction', [r.txHash]]; }); + batchRPCcommand.push(['getaccount', [poolOptions.address]]); + daemon.batchCmd(batchRPCcommand, function(error, txDetails){ if (error || !txDetails){ @@ -224,7 +226,15 @@ function SetupForPool(logger, poolOptions, setupFinished){ return; } + var addressAccount; + txDetails.forEach(function(tx, i){ + + if (i === txDetails.length - 1){ + addressAccount = tx.result; + return; + } + var round = rounds[i]; if (tx.error && tx.error.code === -5 || round.blockHash !== tx.result.blockhash){ @@ -273,7 +283,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ rpc.gettransaction.amount tells us how much we get in whole coin units. Therefore, we simply divide the two to get the magnitude. I don't know math, there is probably a better term than 'magnitude'. Sue me or do a pull request to fix it. */ - var roundMagnitude = r.reward / r.amount; + var roundMagnitude = Math.round(r.reward / r.amount); if (!magnitude) { magnitude = roundMagnitude; @@ -303,7 +313,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ callback('Check finished - no confirmed or orphaned blocks found'); } else{ - callback(null, rounds, magnitude); + callback(null, rounds, magnitude, addressAccount); } }); }, @@ -311,7 +321,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ /* Does a batch redis call to get shares contributed to each round. Then calculates the reward amount owned to each miner for each round. */ - function(rounds, magnitude, callback){ + function(rounds, magnitude, addressAccount, callback){ var shareLookups = rounds.map(function(r){ @@ -370,13 +380,13 @@ function SetupForPool(logger, poolOptions, setupFinished){ }); - callback(null, rounds, magnitude, workerRewards, orphanMergeCommands); + callback(null, rounds, magnitude, workerRewards, orphanMergeCommands, addressAccount); }); }, /* Does a batch call to redis to get worker existing balances from coin_balances*/ - function(rounds, magnitude, workerRewards, orphanMergeCommands, callback){ + function(rounds, magnitude, workerRewards, orphanMergeCommands, addressAccount, callback){ var workers = Object.keys(workerRewards); @@ -394,7 +404,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ } - callback(null, rounds, magnitude, workerRewards, orphanMergeCommands, workerBalances); + callback(null, rounds, magnitude, workerRewards, orphanMergeCommands, workerBalances, addressAccount); }); }, @@ -406,12 +416,11 @@ function SetupForPool(logger, poolOptions, setupFinished){ when deciding the sent balance, it the difference should be -1*amount they had in db, if not sending the balance, the differnce should be +(the amount they earned this round) */ - function(rounds, magnitude, workerRewards, orphanMergeCommands, workerBalances, callback){ + function(rounds, magnitude, workerRewards, orphanMergeCommands, workerBalances, addressAccount, callback){ //number of satoshis in a single coin unit - this can be different for coins so we calculate it :) - - daemon.cmd('getbalance', [''], function(results){ + daemon.cmd('getbalance', [addressAccount || ''], function(results){ var totalBalance = results[0].response * magnitude; var toBePaid = 0; @@ -474,9 +483,9 @@ function SetupForPool(logger, poolOptions, setupFinished){ /* TODO: Need to convert all these variables into whole coin units before displaying because humans aren't good at reading satoshi units. */ callback('Check finished - payments would wipe out minimum reserve, tried to pay out ' + - toBePaid + ' and collect ' + feeAmountToBeCollected + ' as fees' + - ' but only have ' + totalBalance + '. Left over balance would be ' + balanceLeftOver + - ', needs to be at least ' + minReserveSatoshis); + (toBePaid/magnitude) + ' and collect ' + (feeAmountToBeCollected/magnitude) + ' as fees' + + ' but only have ' + (totalBalance/magnitude) + '. Left over balance would be ' + (balanceLeftOver/magnitude) + + ', needs to be at least ' + (minReserveSatoshis/magnitude)); return; } @@ -522,23 +531,23 @@ function SetupForPool(logger, poolOptions, setupFinished){ finalRedisCommands.push(['bgsave']); - callback(null, magnitude, workerPayments, finalRedisCommands); + callback(null, magnitude, workerPayments, finalRedisCommands, addressAccount); }); }, - function(magnitude, workerPayments, finalRedisCommands, callback) { + function(magnitude, workerPayments, finalRedisCommands, addressAccount, 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); + callback(null, magnitude, workerPayments, finalRedisCommands, addressAccount); }); }, - function(magnitude, workerPayments, finalRedisCommands, callback){ + function(magnitude, workerPayments, finalRedisCommands, addressAccount, callback){ //This does the final all-or-nothing atom transaction if block deamon sent payments var finalizeRedisTx = function(){ @@ -570,7 +579,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){ + daemon.cmd('sendmany', [addressAccount || '', addressAmounts], function(results){ if (results[0].error){ callback('Check finished - error with sendmany ' + JSON.stringify(results[0].error)); @@ -591,7 +600,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ } var feeAmountUnits = parseFloat((totalAmountUnits / (1 - processingConfig.feePercent) * processingConfig.feePercent).toFixed(coinPrecision)); var poolFees = feeAmountUnits - results[0].response.fee; - daemon.cmd('move', ['', processingConfig.feeCollectAccount, poolFees], function(results){ + daemon.cmd('move', [addressAccount || '', processingConfig.feeCollectAccount, poolFees], function(results){ if (results[0].error){ callback('Check finished - error with move ' + JSON.stringify(results[0].error)); return; From 86fc01388f8ae332f18b6ad5464a1b173161c774 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 24 Apr 2014 00:02:38 -0600 Subject: [PATCH 14/35] Added crombie's site to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9595242..122c57f 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ If your pool uses NOMP let us know and we will list your website here. * http://miningwith.us * http://kryptochaos.com * http://pool.uberpools.org +* http://onebtcplace.com Usage From 38ececb9ab435a603920672626c9106730bba93d Mon Sep 17 00:00:00 2001 From: Ian Carroll Date: Thu, 24 Apr 2014 16:07:46 -0400 Subject: [PATCH 15/35] gotta represent --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 122c57f..ac8eec5 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ 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 Usage ===== From f1627f222de7908e7c63bcca3e7b71e2b02c1a59 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 24 Apr 2014 14:53:06 -0600 Subject: [PATCH 16/35] Possible fix for payment processing with x11 coins --- libs/paymentProcessor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js index 77cf865..851f121 100644 --- a/libs/paymentProcessor.js +++ b/libs/paymentProcessor.js @@ -366,11 +366,11 @@ function SetupForPool(logger, poolOptions, setupFinished){ var reward = round.reward * (1 - processingConfig.feePercent); var totalShares = Object.keys(workerShares).reduce(function(p, c){ - return p + parseInt(workerShares[c]) + return p + parseFloat(workerShares[c]) }, 0); for (var worker in workerShares){ - var percent = parseInt(workerShares[worker]) / totalShares; + var percent = parseFloat(workerShares[worker]) / totalShares; var workerRewardTotal = Math.floor(reward * percent); if (!(worker in workerRewards)) workerRewards[worker] = 0; workerRewards[worker] += workerRewardTotal; From 444fc6c0700808c10f618e4a0e2329df2014869f Mon Sep 17 00:00:00 2001 From: Alex Petrov Date: Thu, 24 Apr 2014 19:19:14 -0400 Subject: [PATCH 17/35] Added new tab, Table statistics per coin/algo --- libs/website.js | 3 ++- website/index.html | 8 +++++- website/pages/api.html | 7 ++++++ website/pages/tbs.html | 56 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 website/pages/tbs.html diff --git a/libs/website.js b/libs/website.js index 44d9b55..6ac1962 100644 --- a/libs/website.js +++ b/libs/website.js @@ -31,6 +31,7 @@ module.exports = function(logger){ 'home.html': '', 'getting_started.html': 'getting_started', 'stats.html': 'stats', + 'tbs.html': 'tbs', 'api.html': 'api', 'admin.html': 'admin' }; @@ -181,4 +182,4 @@ module.exports = function(logger){ }); -}; \ No newline at end of file +}; diff --git a/website/index.html b/website/index.html index 0542569..03544ac 100644 --- a/website/index.html +++ b/website/index.html @@ -42,7 +42,13 @@
  •   - Stats + Graph Stats + +
  • +
  • + +   + Tab Stats
  • diff --git a/website/pages/api.html b/website/pages/api.html index 6eaea98..9cb343e 100644 --- a/website/pages/api.html +++ b/website/pages/api.html @@ -1,3 +1,10 @@
    API Docs here + +
      +
    • /stats - raw json statistic
    • +
    • /pool_stats - historical time per pool json
    • +
    • /live_stats - live stats
    • + +
    \ No newline at end of file diff --git a/website/pages/tbs.html b/website/pages/tbs.html new file mode 100644 index 0000000..ccc1b3b --- /dev/null +++ b/website/pages/tbs.html @@ -0,0 +1,56 @@ + + From 455da9b5d73e530da7af0a4421f631199787e53d Mon Sep 17 00:00:00 2001 From: Alex Petrov Date: Thu, 24 Apr 2014 19:44:36 -0400 Subject: [PATCH 18/35] New coins added asiccoin,battlecoin,benjamins,betacoin,devcoin,emark,fireflycoin,freicoin,ixcoin,joulecoin,mazacoin,opensourcecoin,takcoin,teacoin,tekcoin,tigercoin --- coins/asiccoin.json | 5 +++++ coins/battlecoin.json | 5 +++++ coins/benjamins.json | 5 +++++ coins/betacoin.json | 5 +++++ coins/devcoin.json | 5 +++++ coins/emark.json | 5 +++++ coins/fireflycoin.json | 5 +++++ coins/freicoin.json | 5 +++++ coins/ixcoin.json | 5 +++++ coins/joulecoin.json | 5 +++++ coins/mazacoin.json | 5 +++++ coins/opensourcecoin.json | 5 +++++ coins/takcoin.json | 5 +++++ coins/teacoin.json | 5 +++++ coins/tekcoin.json | 5 +++++ coins/tigercoin.json | 5 +++++ 16 files changed, 80 insertions(+) create mode 100644 coins/asiccoin.json create mode 100644 coins/battlecoin.json create mode 100644 coins/benjamins.json create mode 100644 coins/betacoin.json create mode 100644 coins/devcoin.json create mode 100644 coins/emark.json create mode 100644 coins/fireflycoin.json create mode 100644 coins/freicoin.json create mode 100644 coins/ixcoin.json create mode 100644 coins/joulecoin.json create mode 100644 coins/mazacoin.json create mode 100644 coins/opensourcecoin.json create mode 100644 coins/takcoin.json create mode 100644 coins/teacoin.json create mode 100644 coins/tekcoin.json create mode 100644 coins/tigercoin.json diff --git a/coins/asiccoin.json b/coins/asiccoin.json new file mode 100644 index 0000000..3c039b9 --- /dev/null +++ b/coins/asiccoin.json @@ -0,0 +1,5 @@ +{ + "name": "ASICcoin", + "symbol": "ASC", + "algorithm": "sha256" +} diff --git a/coins/battlecoin.json b/coins/battlecoin.json new file mode 100644 index 0000000..1e7fd9e --- /dev/null +++ b/coins/battlecoin.json @@ -0,0 +1,5 @@ +{ + "name": "Battlecoin", + "symbol": "BCX", + "algorithm": "sha256" +} diff --git a/coins/benjamins.json b/coins/benjamins.json new file mode 100644 index 0000000..e73a1b6 --- /dev/null +++ b/coins/benjamins.json @@ -0,0 +1,5 @@ +{ + "name": "Benjamins", + "symbol": "BEN", + "algorithm": "sha256" +} diff --git a/coins/betacoin.json b/coins/betacoin.json new file mode 100644 index 0000000..b98efcd --- /dev/null +++ b/coins/betacoin.json @@ -0,0 +1,5 @@ +{ + "name": "Betacoin", + "symbol": "BET", + "algorithm": "sha256" +} diff --git a/coins/devcoin.json b/coins/devcoin.json new file mode 100644 index 0000000..fcbf0f0 --- /dev/null +++ b/coins/devcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Devcoin", + "symbol": "DVC", + "algorithm": "sha256" +} diff --git a/coins/emark.json b/coins/emark.json new file mode 100644 index 0000000..d14cd3e --- /dev/null +++ b/coins/emark.json @@ -0,0 +1,5 @@ +{ + "name": "eMark", + "symbol": "DEM", + "algorithm": "sha256" +} diff --git a/coins/fireflycoin.json b/coins/fireflycoin.json new file mode 100644 index 0000000..98c3eb6 --- /dev/null +++ b/coins/fireflycoin.json @@ -0,0 +1,5 @@ +{ + "name": "Fireflycoin", + "symbol": "FFC", + "algorithm": "sha256" +} diff --git a/coins/freicoin.json b/coins/freicoin.json new file mode 100644 index 0000000..8bf60d9 --- /dev/null +++ b/coins/freicoin.json @@ -0,0 +1,5 @@ +{ + "name": "Freicoin", + "symbol": "FRC", + "algorithm": "sha256" +} diff --git a/coins/ixcoin.json b/coins/ixcoin.json new file mode 100644 index 0000000..80ad2eb --- /dev/null +++ b/coins/ixcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Ixcoin", + "symbol": "IXC", + "algorithm": "sha256" +} diff --git a/coins/joulecoin.json b/coins/joulecoin.json new file mode 100644 index 0000000..1500432 --- /dev/null +++ b/coins/joulecoin.json @@ -0,0 +1,5 @@ +{ + "name": "Joulecoin", + "symbol": "XJO", + "algorithm": "sha256" +} diff --git a/coins/mazacoin.json b/coins/mazacoin.json new file mode 100644 index 0000000..451738c --- /dev/null +++ b/coins/mazacoin.json @@ -0,0 +1,5 @@ +{ + "name": "Mazacoin", + "symbol": "MZC", + "algorithm": "sha256" +} diff --git a/coins/opensourcecoin.json b/coins/opensourcecoin.json new file mode 100644 index 0000000..2867ae8 --- /dev/null +++ b/coins/opensourcecoin.json @@ -0,0 +1,5 @@ +{ + "name": "OpenSourcecoin", + "symbol": "OSC", + "algorithm": "sha256"" +} diff --git a/coins/takcoin.json b/coins/takcoin.json new file mode 100644 index 0000000..d84507c --- /dev/null +++ b/coins/takcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Takcoin", + "symbol": "TAK", + "algorithm": "sha256" +} diff --git a/coins/teacoin.json b/coins/teacoin.json new file mode 100644 index 0000000..c6a0826 --- /dev/null +++ b/coins/teacoin.json @@ -0,0 +1,5 @@ +{ + "name": "Teacoin", + "symbol": "TEA", + "algorithm": "sha256" +} diff --git a/coins/tekcoin.json b/coins/tekcoin.json new file mode 100644 index 0000000..44ba31b --- /dev/null +++ b/coins/tekcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Tekcoin", + "symbol": "TEK", + "algorithm": "sha256" +} diff --git a/coins/tigercoin.json b/coins/tigercoin.json new file mode 100644 index 0000000..6edd7e3 --- /dev/null +++ b/coins/tigercoin.json @@ -0,0 +1,5 @@ +{ + "name": "Tigercoin", + "symbol": "TGC", + "algorithm": "sha256" +} From c6b978d88dfc3d7c7e5aca68a3827d932ef07fcc Mon Sep 17 00:00:00 2001 From: Ian Carroll Date: Thu, 24 Apr 2014 20:02:44 -0400 Subject: [PATCH 19/35] Add FedoraCoin/TiPS --- coins/fedoracoin.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 coins/fedoracoin.json diff --git a/coins/fedoracoin.json b/coins/fedoracoin.json new file mode 100644 index 0000000..a383262 --- /dev/null +++ b/coins/fedoracoin.json @@ -0,0 +1,5 @@ +{ + "name": "FedoraCoin", + "symbol": "TiPS", + "algorithm": "scrypt" +} From 952c7105cc7f91decc4b54be349d61a17bbcd23d Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 26 Apr 2014 16:23:23 -0600 Subject: [PATCH 20/35] Replaced various listeners (coinswitch and blocknotify) with united NOMP CLI (command-line interface) --- README.md | 37 ++----- coins/ecoin.json | 7 -- config_example.json | 70 ++++++------ init.js | 107 ++++++++++++++---- libs/blocknotifyListener.js | 69 ------------ libs/cliListener.js | 40 +++++++ libs/coinswitchListener.js | 56 ---------- libs/poolWorker.js | 211 ++++++++++++++++++++++-------------- libs/stats.js | 6 +- scripts/blockNotify.js | 34 ------ scripts/blocknotify.c | 89 +++++++-------- scripts/cli.js | 38 +++++++ scripts/coinSwitch.js | 37 ------- website/pages/tbs.html | 67 ++++++------ 14 files changed, 422 insertions(+), 446 deletions(-) delete mode 100644 coins/ecoin.json delete mode 100644 libs/blocknotifyListener.js create mode 100644 libs/cliListener.js delete mode 100644 libs/coinswitchListener.js delete mode 100644 scripts/blockNotify.js create mode 100644 scripts/cli.js delete mode 100644 scripts/coinSwitch.js 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}}
    From 4413bfd7e6bb410026cd050dd473178a22b5b212 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 26 Apr 2014 16:24:06 -0600 Subject: [PATCH 21/35] Mining with public key on a switching port will now do payouts. --- libs/paymentProcessor.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js index 851f121..6e05767 100644 --- a/libs/paymentProcessor.js +++ b/libs/paymentProcessor.js @@ -2,7 +2,7 @@ var redis = require('redis'); var async = require('async'); var Stratum = require('stratum-pool'); - +var util = require('stratum-pool/lib/util.js'); module.exports = function(logger){ @@ -38,8 +38,6 @@ module.exports = function(logger){ }); }); - - }; @@ -572,7 +570,12 @@ function SetupForPool(logger, poolOptions, setupFinished){ var totalAmountUnits = 0; for (var address in workerPayments){ var coinUnits = toPrecision(workerPayments[address] / magnitude, coinPrecision); - addressAmounts[address] = coinUnits; + var properAddress = getProperAddress(address); + if (!properAddress){ + logger.error(logSystem, logComponent, 'Could not convert pubkey ' + address + ' into address'); + continue; + } + addressAmounts[properAddress] = coinUnits; totalAmountUnits += coinUnits; } @@ -628,6 +631,13 @@ function SetupForPool(logger, poolOptions, setupFinished){ }; + var getProperAddress = function(address){ + if (address.length === 40){ + return util.addressFromEx(poolOptions.address, address); + } + else return address; + }; + var withdrawalProfit = function(){ if (!processingConfig.feeWithdrawalThreshold) return; @@ -661,5 +671,4 @@ function SetupForPool(logger, poolOptions, setupFinished){ }); }; - -}; \ No newline at end of file +} \ No newline at end of file From f47b023bd7ea5b791926b5a96cfea3566827b491 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 26 Apr 2014 16:31:27 -0600 Subject: [PATCH 22/35] Updated readme for switching config --- README.md | 73 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7b8f582..dd27db8 100644 --- a/README.md +++ b/README.md @@ -212,43 +212,66 @@ Explanation for each field: }, - /* 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 + /* With this switching 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 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. */ - "proxy": { - "sha256": { + Miners connecting to these switching ports must use their public key in the format of + RIPEMD160(SHA256(public-key)). An address for each type of coin is derived from the miner's + public key, and payments are sent to that address. */ + "switching": { + "switch1": { "enabled": false, - "port": "3333", - "diff": 10, - "varDiff": { - "minDiff": 16, //Minimum difficulty - "maxDiff": 512, //Network difficulty will be used if it is lower than this - "targetTime": 15, //Try to get 1 share per this many seconds - "retargetTime": 90, //Check to see if we should retarget every this many seconds - "variancePercent": 30 //Allow time to very this % from target without retargeting + "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, //Minimum difficulty - "maxDiff": 512, //Network difficulty will be used if it is lower than this - "targetTime": 15, //Try to get 1 share per this many seconds - "retargetTime": 90, //Check to see if we should retarget every this many seconds - "variancePercent": 30 //Allow time to very this % from target without retargeting + "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 + } + } } + }, + + "profitSwitch": { + "enabled": false, + "updateInterval": 600, + "depth": 0.90, + "usePoloniex": true, + "useCryptsy": true, + "useMintpal": true } } ```` From 9ba337f33eac8b02ae4eb62eccc19b6ebf130a62 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 26 Apr 2014 16:52:47 -0600 Subject: [PATCH 23/35] Updated profit switching in readme --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dd27db8..6720632 100644 --- a/README.md +++ b/README.md @@ -51,11 +51,8 @@ Each and every share will be rewarded - even for rounds resulting in orphaned bl authentication. A minimalistic HTML5 front-end connects to the portals statistics API to display stats from from each pool such as connected miners, network/pool difficulty/hash rate, etc. -* Automated switching of connected miners to different pools/coins is also easily done due to the multi-pool architecture -of this software. To use this feature the switching must be controlled by your own script, such as one that calculates -coin profitability via an API such as CoinChoose.com or CoinWarz.com (or calculated locally using daemon-reported network -difficulties and exchange APIs). NOMP's regular payment processing and miner authentication which using coin address as stratum -username will obviously not work with this coin switching feature - so you must control those with your own script as well. +* Coin-switching ports using coin-networks and crypto-exchange APIs to detect profitability. Miner's connect to these ports +with their public key which NOMP uses to derive an address for any coin needed to be paid out. #### Attack Mitigation From 52108e3b7ad32a863876fe911a02ec77b082ca36 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 27 Apr 2014 14:24:40 -0600 Subject: [PATCH 24/35] Added mysql connection pooling for reconnecting to db and better performance --- libs/mposCompatibility.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index d94c8cd..21c378d 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -5,13 +5,23 @@ module.exports = function(logger, poolConfig){ var mposConfig = poolConfig.shareProcessing.mpos; var coin = poolConfig.coin.name; - var connection; + //var connection; + var logIdentify = 'MySQL'; var logComponent = coin; function connect(){ - connection = mysql.createConnection({ + + connection = mysql.createPool({ + host: mposConfig.host, + port: mposConfig.port, + user: mposConfig.user, + password: mposConfig.password, + database: mposConfig.database + }); + + /*connection = mysql.createConnection({ host: mposConfig.host, port: mposConfig.port, user: mposConfig.user, @@ -33,7 +43,9 @@ module.exports = function(logger, poolConfig){ else{ logger.error(logIdentify, logComponent, 'Database error: ' + JSON.stringify(err)) } - }); + });*/ + + } connect(); From 25406582b9b17528174f12b322081d4352f4339e Mon Sep 17 00:00:00 2001 From: Alejandro Reyero Date: Mon, 28 Apr 2014 09:54:24 +0200 Subject: [PATCH 25/35] Update api.html Missing closing tag in
  • pool_stats --- website/pages/api.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/pages/api.html b/website/pages/api.html index 9cb343e..b0f73c6 100644 --- a/website/pages/api.html +++ b/website/pages/api.html @@ -3,8 +3,8 @@
    • /stats - raw json statistic
    • -
    • /pool_stats - historical time per pool json
    • +
    • /pool_stats - historical time per pool json
    • /live_stats - live stats
    - \ No newline at end of file + From 66d7640c9b035600c8a39f395bb939369c25820c Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 28 Apr 2014 15:04:12 -0600 Subject: [PATCH 26/35] NOMP CLI gives replies now. Profit switching transitioned to using CLI port rather than old profitListener port. --- README.md | 7 +- init.js | 148 +++++++++++++++++++++++++------------- libs/cliListener.js | 8 ++- libs/mposCompatibility.js | 26 +------ libs/poolWorker.js | 20 +----- libs/profitSwitch.js | 74 ++++++++++--------- scripts/cli.js | 1 - 7 files changed, 149 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 6720632..eeb7214 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ coins at once. The pools use clustering to load balance across multiple CPU core * For reward/payment processing, shares are inserted into Redis (a fast NoSQL key/value store). The PROP (proportional) reward system is used with [Redis Transactions](http://redis.io/topics/transactions) for secure and super speedy payouts. -Each and every share will be rewarded - even for rounds resulting in orphaned blocks. +There is zero risk to the pool operator. Shares from rounds resulting in orphaned blocks will be merged into share in the +current round so that each and every share will be rewarded * This portal does not have user accounts/logins/registrations. Instead, miners simply use their coin address for stratum authentication. A minimalistic HTML5 front-end connects to the portals statistics API to display stats from from each @@ -108,10 +109,12 @@ If your pool uses NOMP let us know and we will list your website here. * http://teamdoge.com * http://miningwith.us * http://kryptochaos.com -* http://pool.uberpools.org +* http://uberpools.org * http://onebtcplace.com * http://minr.es * http://mining.theminingpools.com +* http://www.omargpools.ca/pools.html +* http://pool.trademybit.com/ Usage ===== diff --git a/init.js b/init.js index 4d086ce..bf5c0c9 100644 --- a/init.js +++ b/init.js @@ -22,6 +22,7 @@ if (!fs.existsSync('config.json')){ } var portalConfig = JSON.parse(JSON.minify(fs.readFileSync("config.json", {encoding: 'utf8'}))); +var poolConfigs; var logger = new PoolLogger({ @@ -156,7 +157,7 @@ var buildPoolConfigs = function(){ -var spawnPoolWorkers = function(portalConfig, poolConfigs){ +var spawnPoolWorkers = function(){ Object.keys(poolConfigs).forEach(function(coin){ var p = poolConfigs[coin]; @@ -179,9 +180,6 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ return; } - for (var p in poolConfigs){ - - } var serializedConfigs = JSON.stringify(poolConfigs); @@ -238,61 +236,111 @@ var spawnPoolWorkers = function(portalConfig, poolConfigs){ }; -var startCliListener = function(cliPort){ +var startCliListener = function(){ + + var cliPort = portalConfig.cliPort; + var listener = new CliListener(cliPort); listener.on('log', function(text){ logger.debug('Master', 'CLI', text); - }).on('command', function(command, params, options){ + }).on('command', function(command, params, options, reply){ switch(command){ case 'blocknotify': Object.keys(cluster.workers).forEach(function(id) { cluster.workers[id].send({type: 'blocknotify', coin: params[0], hash: params[1]}); }); + reply('Pool workers notified'); break; case 'coinswitch': - Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].send({type: 'coinswitch', switchName: params[0], coin: params[1] }); - }); + processCoinSwitchCommand(params, options, reply); break; - case 'restartpool': + case 'reloadpool': Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].send({type: 'restartpool', coin: params[0] }); + cluster.workers[id].send({type: 'reloadpool', coin: params[0] }); }); + reply('reloaded pool ' + params[0]); + break; + default: + reply('unrecognized command "' + command + '"'); + break; } - - 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. -// -var startCoinswitchListener = function(portalConfig){ - var listener = new CoinswitchListener(portalConfig.coinSwitchListener); - listener.on('log', function(text){ - logger.debug('Master', 'Coinswitch', text); - }); - listener.on('switchcoin', function(message){ - var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash}; - Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].send(ipcMessage); - }); - var ipcMessage = { - type:'coinswitch', - coin: message.coin - }; - Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].send(ipcMessage); - }); - }); - listener.start(); -}; -*/ -var startRedisBlockListener = function(portalConfig){ +var processCoinSwitchCommand = function(params, options, reply){ + + var logSystem = 'CLI'; + var logComponent = 'coinswitch'; + + var replyError = function(msg){ + reply(msg); + logger.error(logSystem, logComponent, msg); + }; + + if (!params[0]) { + replyError('Coin name required'); + return; + } + + if (!params[1] && !options.algorithm){ + replyError('If switch key is not provided then algorithm options must be specified'); + return; + } + else if (params[1] && !portalConfig.switching[params[1]]){ + replyError('Switch key not recognized: ' + params[1]); + return; + } + else if (options.algorithm && !Object.keys(portalConfig.switching).filter(function(s){ + return portalConfig.switching[s].algorithm === options.algorithm; + })[0]){ + replyError('No switching options contain the algorithm ' + options.algorithm); + return; + } + + var messageCoin = params[0].toLowerCase(); + var newCoin = Object.keys(poolConfigs).filter(function(p){ + return p.toLowerCase() === messageCoin; + })[0]; + + if (!newCoin){ + replyError('Switch message to coin that is not recognized: ' + messageCoin); + return; + } + + + var switchNames = []; + + if (params[1]) { + switchNames.push(params[1]); + } + else{ + for (var name in portalConfig.switching){ + if (portalConfig.switching[name].enabled && portalConfig.switching[name].algorithm === options.algorithm) + switchNames.push(name); + } + } + + switchNames.forEach(function(name){ + if (poolConfigs[newCoin].coin.algorithm !== portalConfig.switching[name].algorithm){ + replyError('Cannot switch a ' + + portalConfig.switching[name].algorithm + + ' algo pool to coin ' + newCoin + ' with ' + poolConfigs[newCoin].coin.algorithm + ' algo'); + return; + } + + Object.keys(cluster.workers).forEach(function (id) { + cluster.workers[id].send({type: 'coinswitch', coin: newCoin, switchName: name }); + }); + }); + + reply('Switch message sent to pool workers'); + +}; + + +var startRedisBlockListener = function(){ //block notify options //setup block notify here and use IPC to tell appropriate pools @@ -311,7 +359,7 @@ var startRedisBlockListener = function(portalConfig){ }; -var startPaymentProcessor = function(poolConfigs){ +var startPaymentProcessor = function(){ var enabledForAny = false; for (var pool in poolConfigs){ @@ -339,7 +387,7 @@ var startPaymentProcessor = function(poolConfigs){ }; -var startWebsite = function(portalConfig, poolConfigs){ +var startWebsite = function(){ if (!portalConfig.website.enabled) return; @@ -357,7 +405,7 @@ var startWebsite = function(portalConfig, poolConfigs){ }; -var startProfitSwitch = function(portalConfig, poolConfigs){ +var startProfitSwitch = function(){ if (!portalConfig.profitSwitch || !portalConfig.profitSwitch.enabled){ //logger.error('Master', 'Profit', 'Profit auto switching disabled'); @@ -381,18 +429,18 @@ var startProfitSwitch = function(portalConfig, poolConfigs){ (function init(){ - var poolConfigs = buildPoolConfigs(); + poolConfigs = buildPoolConfigs(); - spawnPoolWorkers(portalConfig, poolConfigs); + spawnPoolWorkers(); - startPaymentProcessor(poolConfigs); + startPaymentProcessor(); - startRedisBlockListener(portalConfig); + startRedisBlockListener(); - startWebsite(portalConfig, poolConfigs); + startWebsite(); - startProfitSwitch(portalConfig, poolConfigs); + startProfitSwitch(); - startCliListener(portalConfig.cliPort); + startCliListener(); })(); diff --git a/libs/cliListener.js b/libs/cliListener.js index 96744b5..efb18cf 100644 --- a/libs/cliListener.js +++ b/libs/cliListener.js @@ -18,12 +18,14 @@ var listener = module.exports = function listener(port){ c.on('data', function (d) { data += d; if (data.slice(-1) === '\n') { - c.end(); + var message = JSON.parse(data); + _this.emit('command', message.command, message.params, message.options, function(message){ + c.end(message); + }); } }); c.on('end', function () { - var message = JSON.parse(data); - _this.emit('command', message.command, message.params, message.options); + }); } catch(e){ diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index 21c378d..0a3b1c7 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -5,7 +5,7 @@ module.exports = function(logger, poolConfig){ var mposConfig = poolConfig.shareProcessing.mpos; var coin = poolConfig.coin.name; - //var connection; + var connection; var logIdentify = 'MySQL'; @@ -21,30 +21,6 @@ module.exports = function(logger, poolConfig){ database: mposConfig.database }); - /*connection = mysql.createConnection({ - host: mposConfig.host, - port: mposConfig.port, - user: mposConfig.user, - password: mposConfig.password, - database: mposConfig.database - }); - connection.connect(function(err){ - if (err) - logger.error(logIdentify, logComponent, 'Could not connect to mysql database: ' + JSON.stringify(err)) - else{ - logger.debug(logIdentify, logComponent, 'Successful connection to MySQL database'); - } - }); - connection.on('error', function(err){ - if(err.code === 'PROTOCOL_CONNECTION_LOST') { - logger.warning(logIdentify, logComponent, 'Lost connection to MySQL database, attempting reconnection...'); - connect(); - } - else{ - logger.error(logIdentify, logComponent, 'Database error: ' + JSON.stringify(err)) - } - });*/ - } connect(); diff --git a/libs/poolWorker.js b/libs/poolWorker.js index 5c66279..2fc114d 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -50,29 +50,11 @@ module.exports = function(logger){ 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.error(logSystem, logComponent, logSubCat, 'Switch message to coin that is not recognized: ' + messageCoin); - break; - } + var newCoin = message.coin; 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[switchName].currentPool; var oldPool = pools[oldCoin]; diff --git a/libs/profitSwitch.js b/libs/profitSwitch.js index 83491a0..6e0e6c6 100644 --- a/libs/profitSwitch.js +++ b/libs/profitSwitch.js @@ -420,31 +420,28 @@ module.exports = function(logger){ }; this.getDaemonInfoForCoin = function(symbol, cfg, callback){ var daemon = new Stratum.daemon.interface([cfg]); - daemon.once('online', function(){ - daemon.cmd('getblocktemplate', [{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}], function(result){ - if (result[0].error != null){ - logger.error(logSystem, symbol, 'Error while reading daemon info: ' + JSON.stringify(result[0])); - callback(null); // fail gracefully for each coin - return; - } - var coinStatus = profitStatus[symbolToAlgorithmMap[symbol]][symbol]; - var response = result[0].response; - - // some shitcoins dont provide target, only bits, so we need to deal with both - var target = response.target ? bignum(response.target, 16) : util.bignumFromBitsHex(response.bits); - coinStatus.difficulty = parseFloat((diff1 / target.toNumber()).toFixed(9)); - logger.debug(logSystem, symbol, 'difficulty is ' + coinStatus.difficulty); - - coinStatus.reward = new Number(response.coinbasevalue / 100000000); - callback(null); - }); - }).once('connectionFailed', function(error){ - logger.error(logSystem, symbol, JSON.stringify(error)); - callback(null); // fail gracefully for each coin - }).on('error', function(error){ + daemon.on('error', function(error){ logger.error(logSystem, symbol, JSON.stringify(error)); callback(null); // fail gracefully for each coin }).init(); + + daemon.cmd('getblocktemplate', [{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}], function(result) { + if (result[0].error != null) { + logger.error(logSystem, symbol, 'Error while reading daemon info: ' + JSON.stringify(result[0])); + callback(null); // fail gracefully for each coin + return; + } + var coinStatus = profitStatus[symbolToAlgorithmMap[symbol]][symbol]; + var response = result[0].response; + + // some shitcoins dont provide target, only bits, so we need to deal with both + var target = response.target ? bignum(response.target, 16) : util.bignumFromBitsHex(response.bits); + coinStatus.difficulty = parseFloat((diff1 / target.toNumber()).toFixed(9)); + logger.debug(logSystem, symbol, 'difficulty is ' + coinStatus.difficulty); + + coinStatus.reward = response.coinbasevalue / 100000000; + callback(null); + }); }; @@ -453,8 +450,8 @@ module.exports = function(logger){ Object.keys(profitStatus).forEach(function(algo){ Object.keys(profitStatus[algo]).forEach(function(symbol){ var coinStatus = profitStatus[symbolToAlgorithmMap[symbol]][symbol]; - coinStatus.blocksPerMhPerHour = new Number(86400 / ((coinStatus.difficulty * Math.pow(2,32)) / (1 * 1000 * 1000))); - coinStatus.coinsPerMhPerHour = new Number(coinStatus.reward * coinStatus.blocksPerMhPerHour); + coinStatus.blocksPerMhPerHour = 86400 / ((coinStatus.difficulty * Math.pow(2,32)) / (1 * 1000 * 1000)); + coinStatus.coinsPerMhPerHour = coinStatus.reward * coinStatus.blocksPerMhPerHour; }); }); callback(null); @@ -467,7 +464,7 @@ module.exports = function(logger){ var bestExchange; var bestCoin; - var bestBtcPerMhPerHour = new Number(0); + var bestBtcPerMhPerHour = 0; Object.keys(profitStatus[algo]).forEach(function(symbol) { var coinStatus = profitStatus[algo][symbol]; @@ -475,7 +472,7 @@ module.exports = function(logger){ Object.keys(coinStatus.exchangeInfo).forEach(function(exchange){ var exchangeData = coinStatus.exchangeInfo[exchange]; if (exchangeData.hasOwnProperty('BTC') && exchangeData['BTC'].hasOwnProperty('weightedBid')){ - var btcPerMhPerHour = new Number(exchangeData['BTC'].weightedBid * coinStatus.coinsPerMhPerHour); + var btcPerMhPerHour = exchangeData['BTC'].weightedBid * coinStatus.coinsPerMhPerHour; if (btcPerMhPerHour > bestBtcPerMhPerHour){ bestBtcPerMhPerHour = btcPerMhPerHour; bestExchange = exchange; @@ -485,7 +482,7 @@ module.exports = function(logger){ logger.debug(logSystem, 'CALC', 'BTC/' + symbol + ' on ' + exchange + ' with ' + coinStatus.btcPerMhPerHour.toFixed(8) + ' BTC/day per Mh/s'); } if (exchangeData.hasOwnProperty('LTC') && exchangeData['LTC'].hasOwnProperty('weightedBid')){ - var btcPerMhPerHour = new Number((exchangeData['LTC'].weightedBid * coinStatus.coinsPerMhPerHour) * exchangeData['LTC'].ltcToBtc); + var btcPerMhPerHour = (exchangeData['LTC'].weightedBid * coinStatus.coinsPerMhPerHour) * exchangeData['LTC'].ltcToBtc; if (btcPerMhPerHour > bestBtcPerMhPerHour){ bestBtcPerMhPerHour = btcPerMhPerHour; bestExchange = exchange; @@ -497,14 +494,21 @@ module.exports = function(logger){ }); }); logger.debug(logSystem, 'RESULT', 'Best coin for ' + algo + ' is ' + bestCoin + ' on ' + bestExchange + ' with ' + bestBtcPerMhPerHour.toFixed(8) + ' BTC/day per Mh/s'); - if (portalConfig.coinSwitchListener.enabled){ - var client = net.connect(portalConfig.coinSwitchListener.port, portalConfig.coinSwitchListener.host, function () { - client.write(JSON.stringify({ - password: portalConfig.coinSwitchListener.password, - coin: bestCoin - }) + '\n'); - }); - } + + + var client = net.connect(portalConfig.cliPort, function () { + client.write(JSON.stringify({ + command: 'coinswitch', + params: [bestCoin], + options: {algorithm: algo} + }) + '\n'); + }).on('error', function(error){ + if (error.code === 'ECONNREFUSED') + logger.error(logSystem, 'CLI', 'Could not connect to NOMP instance on port ' + portalConfig.cliPort); + else + logger.error(logSystem, 'CLI', 'Socket error ' + JSON.stringify(error)); + }); + }); }; diff --git a/scripts/cli.js b/scripts/cli.js index 013678c..5e1cfd6 100644 --- a/scripts/cli.js +++ b/scripts/cli.js @@ -34,5 +34,4 @@ var client = net.connect(options.port || defaultPort, options.host || defaultHos }).on('data', function(data) { console.log(data.toString()); }).on('close', function () { - }); \ No newline at end of file From 62c9a59b72b2ef7188d919ae7ce16b656282b5e4 Mon Sep 17 00:00:00 2001 From: Alejandro Reyero Date: Tue, 29 Apr 2014 10:56:35 +0200 Subject: [PATCH 27/35] Update tbs.html Added info about Block status --- website/pages/tbs.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/website/pages/tbs.html b/website/pages/tbs.html index 12ce72b..9ca8352 100644 --- a/website/pages/tbs.html +++ b/website/pages/tbs.html @@ -40,7 +40,10 @@ Workers Valid Shares Invalid Shares - Blocks + Total Blocks + Pending + Confirmed + Orphaned Hashrate @@ -53,6 +56,9 @@ {{=it.stats.pools[pool].poolStats.validShares}} {{=it.stats.pools[pool].poolStats.invalidShares}} {{=it.stats.pools[pool].poolStats.validBlocks}} + {{=it.stats.pools[pool].blocks.pending}} + {{=it.stats.pools[pool].blocks.confirmed}} + {{=it.stats.pools[pool].blocks.orphaned}} {{=it.stats.pools[pool].hashrateString}} {{ } }} From 6291ad0b69ff9d330b10416e8d00c99f9afbe3a1 Mon Sep 17 00:00:00 2001 From: Elliot Boney Date: Tue, 29 Apr 2014 16:01:55 +0000 Subject: [PATCH 28/35] added starcoin and safer gitignore --- .gitignore | 4 +++- coins/starcoin.json | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 coins/starcoin.json diff --git a/.gitignore b/.gitignore index 88d9a09..b2f46e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules/ .idea/ -config.json \ No newline at end of file +config.json +pool_configs/*.json +!pool_configs/litecoin_example.json diff --git a/coins/starcoin.json b/coins/starcoin.json new file mode 100644 index 0000000..ca65a81 --- /dev/null +++ b/coins/starcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Starcoin", + "symbol": "STR", + "algorithm": "scrypt" +} From f922a92898a688850ac77ac773c7f45d0b0bb4f4 Mon Sep 17 00:00:00 2001 From: Alejandro Reyero Date: Tue, 29 Apr 2014 23:30:12 +0200 Subject: [PATCH 29/35] Create globalboost.json GlobalBoost Coin --- coins/globalboost.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 coins/globalboost.json diff --git a/coins/globalboost.json b/coins/globalboost.json new file mode 100644 index 0000000..8114798 --- /dev/null +++ b/coins/globalboost.json @@ -0,0 +1,5 @@ +{ + "name": "GlobalBoost", + "symbol": "BST", + "algorithm": "scrypt" +} From b8eb99be215b11bc25b83e6e48f85ee0cc01db66 Mon Sep 17 00:00:00 2001 From: Alejandro Reyero Date: Tue, 29 Apr 2014 23:31:04 +0200 Subject: [PATCH 30/35] Create guarantcoin.json Guarantcoin --- coins/guarantcoin.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 coins/guarantcoin.json diff --git a/coins/guarantcoin.json b/coins/guarantcoin.json new file mode 100644 index 0000000..9c3c114 --- /dev/null +++ b/coins/guarantcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Guarantcoin", + "symbol": "GTC", + "algorithm": "scrypt-n" +} From 059faf4f77a09d318c0adcefc64675f9ad7355a5 Mon Sep 17 00:00:00 2001 From: Alejandro Reyero Date: Tue, 29 Apr 2014 23:32:23 +0200 Subject: [PATCH 31/35] Create globaldenomination.json GlobalDenomination --- coins/globaldenomination.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 coins/globaldenomination.json diff --git a/coins/globaldenomination.json b/coins/globaldenomination.json new file mode 100644 index 0000000..d43c28b --- /dev/null +++ b/coins/globaldenomination.json @@ -0,0 +1,5 @@ +{ + "name": "GlobalDenomination", + "symbol": "GDN", + "algorithm": "x11" +} From 526d184d5e4b029faa82ef687ba75ba8a5662011 Mon Sep 17 00:00:00 2001 From: Alejandro Reyero Date: Tue, 29 Apr 2014 23:32:59 +0200 Subject: [PATCH 32/35] Create giarcoin.json Giarcoin --- coins/giarcoin.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 coins/giarcoin.json diff --git a/coins/giarcoin.json b/coins/giarcoin.json new file mode 100644 index 0000000..e0f592d --- /dev/null +++ b/coins/giarcoin.json @@ -0,0 +1,5 @@ +{ + "name": "Giarcoin", + "symbol": "GIAR", + "algorithm": "scrypt-n" +} From 5ea852d3696847844b6c043f5a393adec4d8df0d Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 30 Apr 2014 20:43:43 -0600 Subject: [PATCH 33/35] Added client-side mining key generation script for coin-switching port mining --- libs/paymentProcessor.js | 5 +- libs/website.js | 99 +- website/key.html | 2798 +++++++++++++++++++++++++++++++++ website/pages/mining_key.html | 25 + 4 files changed, 2923 insertions(+), 4 deletions(-) create mode 100644 website/key.html create mode 100644 website/pages/mining_key.html diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js index 6e05767..638c64d 100644 --- a/libs/paymentProcessor.js +++ b/libs/paymentProcessor.js @@ -502,7 +502,8 @@ function SetupForPool(logger, poolOptions, setupFinished){ } })(); movePendingCommands.push(['smove', coin + '_blocksPending', coin + destinationSet, r.serialized]); - roundsToDelete.push(coin + '_shares:round' + r.height) + if (r.category === 'generate') + roundsToDelete.push(coin + '_shares:round' + r.height) }); var finalRedisCommands = []; @@ -660,7 +661,7 @@ function SetupForPool(logger, poolOptions, setupFinished){ daemon.cmd('sendmany', [processingConfig.feeCollectAccount, withdrawal], function(results){ if (results[0].error){ - logger.debug(logSystem, logComponent, 'Profit withdrawal finished - error with sendmany ' + logger.debug(logSystem, logComponent, 'Profit withdrawal of ' + withdrawalAmount + ' failed - error with sendmany ' + JSON.stringify(results[0].error)); return; } diff --git a/libs/website.js b/libs/website.js index 6ac1962..3af59c5 100644 --- a/libs/website.js +++ b/libs/website.js @@ -3,18 +3,24 @@ var fs = require('fs'); var path = require('path'); var async = require('async'); +var watch = require('node-watch'); +var redis = require('redis'); + var dot = require('dot'); var express = require('express'); var bodyParser = require('body-parser'); var compress = require('compression'); -var watch = require('node-watch'); +var Stratum = require('stratum-pool'); +var util = require('stratum-pool/lib/util.js'); var api = require('./api.js'); module.exports = function(logger){ + dot.templateSettings.strip = false; + var portalConfig = JSON.parse(process.env.portalConfig); var poolConfigs = JSON.parse(process.env.pools); @@ -33,7 +39,8 @@ module.exports = function(logger){ 'stats.html': 'stats', 'tbs.html': 'tbs', 'api.html': 'api', - 'admin.html': 'admin' + 'admin.html': 'admin', + 'mining_key.html': 'mining_key' }; var pageTemplates = {}; @@ -41,6 +48,9 @@ module.exports = function(logger){ var pageProcessed = {}; var indexesProcessed = {}; + var keyScriptTemplate = ''; + var keyScriptProcessed = ''; + var processTemplates = function(){ @@ -113,6 +123,87 @@ module.exports = function(logger){ setInterval(buildUpdatedWebsite, websiteConfig.stats.updateInterval * 1000); + var buildKeyScriptPage = function(){ + async.waterfall([ + function(callback){ + var client = redis.createClient(portalConfig.redis.port, portalConfig.redis.host); + client.hgetall('coinVersionBytes', function(err, coinBytes){ + if (err){ + client.quit(); + return callback('Failed grabbing coin version bytes from redis ' + JSON.stringify(err)); + } + callback(null, client, coinBytes || {}); + }); + }, + function (client, coinBytes, callback){ + var enabledCoins = Object.keys(poolConfigs).map(function(c){return c.toLowerCase()}); + var missingCoins = []; + enabledCoins.forEach(function(c){ + if (!(c in coinBytes)) + missingCoins.push(c); + }); + callback(null, client, coinBytes, missingCoins); + }, + function(client, coinBytes, missingCoins, callback){ + var coinsForRedis = {}; + async.each(missingCoins, function(c, cback){ + var coinInfo = (function(){ + for (var pName in poolConfigs){ + if (pName.toLowerCase() === c) + return { + daemon: poolConfigs[pName].shareProcessing.internal.daemon, + address: poolConfigs[pName].address + } + } + })(); + var daemon = new Stratum.daemon.interface([coinInfo.daemon]); + daemon.cmd('dumpprivkey', [coinInfo.address], function(result){ + if (result[0].error){ + logger.error(logSystem, 'daemon', 'Could not dumpprivkey for ' + c + ' ' + JSON.stringify(result[0].error)); + cback(); + return; + } + + var vBytePub = util.getVersionByte(coinInfo.address)[0]; + var vBytePriv = util.getVersionByte(result[0].response)[0]; + + coinBytes[c] = vBytePub.toString() + ',' + vBytePriv.toString(); + coinsForRedis[c] = coinBytes[c]; + cback(); + }); + }, function(err){ + callback(null, client, coinBytes, coinsForRedis); + }); + }, + function(client, coinBytes, coinsForRedis, callback){ + if (Object.keys(coinsForRedis).length > 0){ + client.hmset('coinVersionBytes', coinsForRedis, function(err){ + if (err) + logger.error(logSystem, 'Init', 'Failed inserting coin byte version into redis ' + JSON.stringify(err)); + client.quit(); + }); + } + else{ + client.quit(); + } + callback(null, coinBytes); + } + ], function(err, coinBytes){ + if (err){ + logger.error(logSystem, 'Init', err); + return; + } + try{ + keyScriptTemplate = dot.template(fs.readFileSync('website/key.html', {encoding: 'utf8'})); + keyScriptProcessed = keyScriptTemplate({coins: coinBytes}); + } + catch(e){ + logger.error(logSystem, 'Init', 'Failed to read key.html file'); + } + }); + + }; + buildKeyScriptPage(); var getPage = function(pageId){ if (pageId in pageProcessed){ @@ -147,6 +238,10 @@ module.exports = function(logger){ next(); }); + app.get('/key.html', function(reg, res, next){ + res.end(keyScriptProcessed); + }); + app.get('/:page', route); app.get('/', route); diff --git a/website/key.html b/website/key.html new file mode 100644 index 0000000..9978e9e --- /dev/null +++ b/website/key.html @@ -0,0 +1,2798 @@ + + + + + + Mining Key Script + + + + + + + + +
    +

    Mining key generation or input options:

    + +
    +
    1)
    +
    Create new private key
    + +
    +
    - or-
    +
    +
    2)
    +
    Import existing private key
    + +
    +
    - or-
    +
    +
    3)
    +
    Input private key hex
    + + +
    Private key must be 64 hexadecimal characters
    +
    +
    + +
    + + + +
    +
    NO NOT LOSE THIS PRIVATE KEY. Any coins mined using this public key can + only be controlled with this private key.
    + +
    Private key:
    +
    Key for mining (hashed public key):
    + +

    + An address for any type of coin can be derived from this mining key - and each of those coin address + can only be controlled by this private key. +

    + +
    + +
    +
    Backup your private key
    + + + + + + + + + +
    Step 1) + +
    Step 2) + +
    +
    + +
    +
    Coin formatted keys
    + + +
    Public address
    + + +
    Private key in wallet import format
    + + +
    +
    How to import your private key for :
    +
      +
    1. Open your wallet app
    2. +
    3. Go to Help -> click Debug window -> click Console tab
    4. +
    5. Enter the following command: importprivkey
    6. +
    +
    + +
    + + +
    + + + + + + + + + \ No newline at end of file diff --git a/website/pages/mining_key.html b/website/pages/mining_key.html new file mode 100644 index 0000000..d380676 --- /dev/null +++ b/website/pages/mining_key.html @@ -0,0 +1,25 @@ + + +
    + +

    + This script run client-side (in your browser). For maximum security download the script and run it locally and + offline in a modern web browser. +

    + + + +
    From 850c2703816290208fe01d1b23a69b41d0820303 Mon Sep 17 00:00:00 2001 From: bitcoinland Date: Thu, 1 May 2014 00:17:19 -0300 Subject: [PATCH 34/35] removed extra " from algorithm --- coins/opensourcecoin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coins/opensourcecoin.json b/coins/opensourcecoin.json index 2867ae8..9b86d03 100644 --- a/coins/opensourcecoin.json +++ b/coins/opensourcecoin.json @@ -1,5 +1,5 @@ { "name": "OpenSourcecoin", "symbol": "OSC", - "algorithm": "sha256"" + "algorithm": "sha256" } From 13a22d0ed63dd20238856185ee188502a36b164d Mon Sep 17 00:00:00 2001 From: thrassos Date: Thu, 1 May 2014 20:01:54 +0300 Subject: [PATCH 35/35] Update poolWorker.js --- libs/poolWorker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/poolWorker.js b/libs/poolWorker.js index 2fc114d..fc310b7 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -284,8 +284,8 @@ module.exports = function(logger){ 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); + if (portalConfig.switching[switchName].ports[port].varDiff) + p.setVarDiff(port, portalConfig.switching[switchName].ports[port].varDiff); } } });