Added mpos auth feature

This commit is contained in:
Matt 2014-03-04 13:24:02 -07:00
parent 95fe6129b3
commit 943f3d1f32
8 changed files with 162 additions and 44 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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){

View File

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

View File

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

View File

@ -23,7 +23,7 @@ var client = net.connect(port, host, function() {
client.write(JSON.stringify({
password: password,
coin: coin,
blockHash: blockHash
hash: blockHash
}) + '\n');
});