From 943f3d1f32a64f52e130c1ac532322eab0d24bed Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 4 Mar 2014 13:24:02 -0700 Subject: [PATCH] Added mpos auth feature --- README.md | 8 ++- init.js | 4 +- libs/mposCompatibility.js | 52 +++++++++++++-- libs/poolWorker.js | 73 ++++++++++++++++++---- libs/shareProcessor.js | 53 ++++++++++------ libs/workerListener.js | 3 + pool_configs/litecoin_testnet_example.json | 11 +++- scripts/blockNotify.js | 2 +- 8 files changed, 162 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 1f8db26..c4b142a 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,11 @@ Description of options: "port": 3306, "user": "me", "password": "mypass", - "database": "ltc" + "database": "ltc", + /* 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 */ + "stratumAuth": "password"' } }, @@ -180,7 +184,7 @@ Description of options: ```` You can create as many of these pool config files as you want (such as one pool per coin you which to operate). -If you are creating multiple pools, ensure that they have unique stratum ports with the `pool.stratumPort` field. +If you are creating multiple pools, ensure that they have unique stratum ports. For more information on these configuration options see the [pool module documentation](https://github.com/zone117x/node-stratum#module-usage) diff --git a/init.js b/init.js index 7dd1f25..3023015 100644 --- a/init.js +++ b/init.js @@ -110,9 +110,9 @@ if (cluster.isMaster){ }); listener.on('hash', function(message){ - var serializedMessage = JSON.stringify({'blocknotify': message.hash}); + var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash}; Object.keys(cluster.workers).forEach(function(id) { - cluster.workers[id].send(serializedMessage); + cluster.workers[id].send(ipcMessage); }); }); diff --git a/libs/mposCompatibility.js b/libs/mposCompatibility.js index 02dafff..ae240d8 100644 --- a/libs/mposCompatibility.js +++ b/libs/mposCompatibility.js @@ -4,6 +4,8 @@ module.exports = function(logger, poolConfigs){ var dbConnections = (function(){ var connections = {}; + + Object.keys(poolConfigs).forEach(function(coin) { var config = poolConfigs[coin]; @@ -23,21 +25,21 @@ module.exports = function(logger, poolConfigs){ }); connection.connect(function(err){ if (err) - logger.logError('shareProcessor', 'mysql', config.coin.name + + logger.logError('shareProcessor', 'mysql', coin + ' - could not connect to mysql database: ' + JSON.stringify(err)) else{ - logger.logDebug('shareProcessor', 'mysql', config.coin.name + + logger.logDebug('shareProcessor', 'mysql', coin + ' - successful connection to MySQL database'); } }); connection.on('error', function(err){ if(err.code === 'PROTOCOL_CONNECTION_LOST') { - logger.logWarning('shareProcessor', 'mysql', config.coin.name + + logger.logWarning('shareProcessor', 'mysql', coin + ' - lost connection to MySQL database, attempting reconnection...'); connect(); } else{ - logger.logError('shareProcessor', 'mysql', config.coin.name + + logger.logError('shareProcessor', 'mysql', coin + ' - mysql database error: ' + JSON.stringify(err)) } }); @@ -45,10 +47,48 @@ module.exports = function(logger, poolConfigs){ connect(); }); return connections; - }); + })(); - this.handleAuth = function(allowCallback){ + this.handleAuth = function(data){ + /* + type: 'mposAuth', + coin: poolOptions.coin.name, + callbackId: callbackId, + workerId: cluster.worker.id, + workerName: workerName, + password: password, + authLevel: authLevel + */ + + var sendResult = function(authorized){ + cluster.workers[data.workerId].send({ + type: 'mposAuth', + callbackId: data.callbackId, + authorized: authorized + }); + }; + + var connection = dbConnections[data.coin]; + connection.query( + 'SELECT password FROM pool_worker WHERE username = LOWER(?)', + [data.workerName], + function(err, result){ + if (err){ + logger.logError('shareProcessor', 'mysql', 'MySQL error when authenticating worker: ' + + JSON.stringify(err)); + sendResult(false); + } + else if (!result[0]) + sendResult(false); + else if (data.authLevel === 'worker') + sendResult(true); + else if (result[0].password === data.password) + sendResult(true) + else + sendResult(false); + } + ); }; diff --git a/libs/poolWorker.js b/libs/poolWorker.js index 7fee87e..beb429a 100644 --- a/libs/poolWorker.js +++ b/libs/poolWorker.js @@ -1,3 +1,5 @@ +var cluster = require('cluster'); + var Stratum = require('stratum-pool'); module.exports = function(logger){ @@ -13,19 +15,28 @@ module.exports = function(logger){ var pools = []; - //Handle blocknotify message from master process sent via IPC + //Handle messages from master process sent via IPC process.on('message', function(message) { - if (message.blocknotify){ - for (var i = 0; i < pools.length; i++){ - if (pools[i].options.coin.name.toLowerCase() === message.coin.toLowerCase()){ - pools[i].processBlockNotify(message.blockHash) - return; + switch(message.type){ + case 'blocknotify': + for (var i = 0; i < pools.length; i++){ + if (pools[i].options.coin.name.toLowerCase() === message.coin.toLowerCase()){ + pools[i].processBlockNotify(message.hash) + return; + } } - } + break; + case 'mposAuth': + var callbackId = message.callbackId; + if (callbackId in mposAuthCallbacks) + mposAuthCallbacks[callbackId](message.authorized); + break; } }); + var mposAuthCallbacks = {}; + Object.keys(poolConfigs).forEach(function(coin) { var poolOptions = poolConfigs[coin]; @@ -35,11 +46,49 @@ module.exports = function(logger){ var authorizeFN = function (ip, workerName, password, callback) { // Default implementation just returns true logDebug(logIdentify, 'client', "Authorize [" + ip + "] " + workerName + ":" + password); - callback({ - error: null, - authorized: true, - disconnect: false - }); + + var mposAuthLevel; + if (poolOptions.shareProcessing.mpos.enabled && ( + (mposAuthLevel = poolOptions.shareProcessing.mpos.stratumAuth) === 'worker' || + mposAuthLevel === 'password' + )){ + var callbackId = coin + workerName + password + Date.now(); + var authTimeout = setTimeout(function(){ + if (!(callbackId in mposAuthCallbacks)) + return; + callback({ + error: null, + authorized: false, + disconnect: false + }); + delete mposAuthCallbacks[callbackId]; + }, 30000); + mposAuthCallbacks[callbackId] = function(authorized){ + callback({ + error: null, + authorized: authorized, + disconnect: false + }); + delete mposAuthCallbacks[callbackId]; + clearTimeout(authTimeout); + }; + process.send({ + type: 'mposAuth', + coin: poolOptions.coin.name, + callbackId: callbackId, + workerId: cluster.worker.id, + workerName: workerName, + password: password, + authLevel: mposAuthLevel + }); + } + else{ + callback({ + error: null, + authorized: true, + disconnect: false + }); + } }; diff --git a/libs/shareProcessor.js b/libs/shareProcessor.js index a96dbcf..83fe0e8 100644 --- a/libs/shareProcessor.js +++ b/libs/shareProcessor.js @@ -3,39 +3,56 @@ var redis = require('redis'); module.exports = function(logger, poolConfigs){ - //TODO: need to add redis config to json. probably do one redis client per pool? + var dbConnections = (function(){ + var connections = {}; + Object.keys(poolConfigs).forEach(function(coin) { - var client; + var config = poolConfigs[coin]; - client = redis.createClient(); + if (!config.shareProcessing || !config.shareProcessing.internal || !config.shareProcessing.internal.enabled) + return; + + var redisConfig = config.shareProcessing.internal.redis; + + function connect(){ + var connection = connections[coin] = redis.createClient(redisConfig.port, redisConfig.host); + connection.on('error', function(err){ + logger.logError('shareProcessor', 'redis', coin + + ' - redis client had an error: ' + JSON.stringify(err)) + }); + connection.on('end', function(){ + logger.logWarning('shareProcessor', 'redis', coin + + ' - connection to redis database as been ended'); + connect(); + }); + } + connect(); + }); + })(); - client.on("error", function (err) { - logger.logError('shareProcessor', 'redis', 'Redis client had an error: ' + err); - }); this.handleDifficultyUpdate = function(data){ var coin = data.coin; var poolConfig = poolConfigs[coin]; - if (poolConfig.shareProcessing.mpos && poolConfig.shareProcessing.mpos.enabled){ - poolMposHandlers[coin].updateDifficulty(data.workerName, data.diff); - } }; this.handleShare = function(data){ + + if ((!data.coin in dbConnections)) return; + + if (!data.isValidShare) return; + + var connection = dbConnections[data.coin]; + var shareData = data.share; var coin = data.coin; var poolConfig = poolConfigs[coin]; - if (poolConfig.shareProcessing.mpos && poolConfig.shareProcessing.mpos.enabled){ - poolMposHandlers[coin].insertShare(data.isValidShare, data.isValidBlock, shareData); - } + connection.hincrby([coin + ':' + shareData.height, shareData.worker, shareData.difficulty], function(error, result){ + if (error) + logger.logError('shareProcessor', 'redis', 'could not store worker share') + }); - if (poolConfig.shareProcessing.internal && poolConfig.shareProcessing.internal.enable && data.isValidShare){ - client.hincrby([coin + ':' + shareData.height, shareData.worker, shareData.difficulty], function(error, result){ - if (error) - logger.logError('shareProcessor', 'redis', 'could not store worker share') - }); - } }; this.handleBlock = function(data){ diff --git a/libs/workerListener.js b/libs/workerListener.js index 634b4d3..92c0329 100644 --- a/libs/workerListener.js +++ b/libs/workerListener.js @@ -36,6 +36,9 @@ var processor = module.exports = function processor(logger, poolConfigs){ if (shareProcessing.internal.enabled) shareProcessor.handleBlock(data); break; + case 'mposAuth': + mposCompat.handleAuth(data); + break; } }); }); diff --git a/pool_configs/litecoin_testnet_example.json b/pool_configs/litecoin_testnet_example.json index e5d5304..1c98549 100644 --- a/pool_configs/litecoin_testnet_example.json +++ b/pool_configs/litecoin_testnet_example.json @@ -22,21 +22,26 @@ "shareProcessing": { "internal": { - "enabled": true, + "enabled": false, "daemon": { "host": "localhost", "port": 19332, "user": "litecoinrpc", "password": "testnet" + }, + "redis": { + "host": "localhost", + "port": 6379 } }, "mpos": { - "enabled": false, + "enabled": true, "host": "localhost", "port": 3306, "user": "me", "password": "mypass", - "database": "ltc" + "database": "ltc", + "stratumAuth": "password" } }, diff --git a/scripts/blockNotify.js b/scripts/blockNotify.js index 37e7909..ae328b8 100644 --- a/scripts/blockNotify.js +++ b/scripts/blockNotify.js @@ -23,7 +23,7 @@ var client = net.connect(port, host, function() { client.write(JSON.stringify({ password: password, coin: coin, - blockHash: blockHash + hash: blockHash }) + '\n'); });