Started mysql code for mpos compat.

This commit is contained in:
Matt 2014-03-03 13:51:11 -07:00
parent 9376e79a38
commit 8e83c32e17
6 changed files with 99 additions and 24 deletions

View File

@ -10,11 +10,11 @@ balance across multiple CPU cores.
For reward/payment processing, shares are inserted into a fast NoSQL key/value database (Redis). Each coin has a For reward/payment processing, shares are inserted into a fast NoSQL key/value database (Redis). Each coin has a
processor that monitors for confirmed submitted blocks then send out payments according to shares accumulated in the processor that monitors for confirmed submitted blocks then send out payments according to shares accumulated in the
database. The payment/reward method used will be PROP (proportional) - where when a block is found, miners are paid database. The payment/reward method used will be PROP (proportional) - where when a block is submitted and confirmed,
based on their shares submitted during the round (a round is the process of searching for a single block). miners are paid based on their shares submitted during the round (a round is the process of searching for a single block).
For those that wish to use this project with [MPOS](https://github.com/MPOS/php-mpos), the portal can be configured For those that wish to use this project with [MPOS](https://github.com/MPOS/php-mpos), the portal can be configured
to insert shares into a MySQL database in the format which MPOS uses. to insert shares into a MySQL database in the format which MPOS expects.
This portal does not have user accounts/logins/registrations. Instead, miners simply use their coin address for stratum 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 authentication. A minimalistic HTML5 front-end connects to the portals statistics API to display stats from from each
@ -92,8 +92,9 @@ Description of options:
"enabled": false, "enabled": false,
"host": "localhost", "host": "localhost",
"port": 3306, "port": 3306,
"name": "doge", "user": "me",
"password": "mypass" "password": "mypass",
"database": "ltc"
}, },
"internal": { //enabled this options for share payments to be processed and sent locally "internal": { //enabled this options for share payments to be processed and sent locally
"enabled": true, "enabled": true,
@ -109,8 +110,8 @@ Description of options:
} }
}, },
//All options below this are passed directly to the stratum module: /* All options below this are passed directly to the stratum module:
https://github.com/zone117x/node-stratum - which has some additional documentation. https://github.com/zone117x/node-stratum - which has some additional documentation. */
"pool": { "pool": {
//instanceId: 37, //Recommend not using this because a crypto-random one will be generated //instanceId: 37, //Recommend not using this because a crypto-random one will be generated

View File

@ -56,7 +56,7 @@ if (cluster.isMaster){
//Read all pool configs from pool_configs and join them with their coin profile //Read all pool configs from pool_configs and join them with their coin profile
var poolConfigs = (function(){ var poolConfigs = (function(){
var configs = []; var configs = {};
fs.readdirSync('pool_configs').forEach(function(file){ fs.readdirSync('pool_configs').forEach(function(file){
var poolOptions = JSON.parse(JSON.minify(fs.readFileSync('pool_configs/' + file, {encoding: 'utf8'}))); var poolOptions = JSON.parse(JSON.minify(fs.readFileSync('pool_configs/' + file, {encoding: 'utf8'})));
if (poolOptions.disabled) return; if (poolOptions.disabled) return;
@ -65,7 +65,7 @@ if (cluster.isMaster){
return; return;
} }
poolOptions.coin = coinProfiles[poolOptions.coin.toLowerCase()]; poolOptions.coin = coinProfiles[poolOptions.coin.toLowerCase()];
configs.push(poolOptions); configs[poolOptions.coin.name] = poolOptions;
}); });
return configs; return configs;
})(); })();

View File

@ -26,11 +26,11 @@ module.exports = function(logger){
}); });
Object.keys(poolConfigs).forEach(function(coin) {
var poolOptions = poolConfigs[coin];
poolConfigs.forEach(function(poolOptions){ var logIdentify = coin + ' (Fork ' + fork + ')';
var logIdentify = poolOptions.coin.name + ' (Fork ' + fork + ')';
var authorizeFN = function (ip, workerName, password, callback) { var authorizeFN = function (ip, workerName, password, callback) {
// Default implementation just returns true // Default implementation just returns true
@ -50,23 +50,26 @@ module.exports = function(logger){
if (data.solution && !isValidBlock){ if (data.solution && !isValidBlock){
logDebug(logIdentify, 'client', 'We thought a block solution was found but it was rejected by the daemon, share data: ' + shareData); logDebug(logIdentify, 'client', 'We thought a block solution was found but it was rejected by the daemon, share data: ' + shareData);
return;
} }
else if (!isValidShare){ else if (!isValidShare){
logDebug(logIdentify, 'client', 'Invalid share submitted, share data: ' + shareData) logDebug(logIdentify, 'client', 'Invalid share submitted, share data: ' + shareData)
return;
} }
logDebug(logIdentify, 'client', 'Valid share submitted, share data: ' + shareData); logDebug(logIdentify, 'client', 'Valid share submitted, share data: ' + shareData);
process.send({type: 'share', share: shareData, coin: poolOptions.coin.name}); process.send({
type: 'share',
share: shareData,
coin: poolOptions.coin.name,
isValidShare: isValidShare,
isValidBlock: isValidBlock
});
if (isValidBlock){ if (isValidBlock){
logDebug(logIdentify, 'client', 'Block found, solution: ' + shareData.solution); logDebug(logIdentify, 'client', 'Block found, solution: ' + shareData.solution);
process.send({ process.send({
type: 'block', type: 'block',
share: shareData, share: shareData,
coin: poolOptions.coin.name, coin: poolOptions.coin.name
confirmations: poolOptions.rewards.blockConfirmations
}); });
} }

View File

@ -2,6 +2,7 @@ var events = require('events');
var cluster = require('cluster'); var cluster = require('cluster');
var redis = require('redis'); var redis = require('redis');
var mysql = require('mysql');
var processor = module.exports = function processor(logger, poolConfigs){ var processor = module.exports = function processor(logger, poolConfigs){
@ -9,13 +10,81 @@ var processor = module.exports = function processor(logger, poolConfigs){
var client; var client;
var poolMposHandlers = (function(){
var handlers = {};
Object.keys(poolConfigs).forEach(function(coin) {
var config = poolConfigs[coin];
if (!config.shareProcessing || !config.shareProcessing.mpos || !config.shareProcessing.mpos.enabled)
return;
var mposConfig = config.shareProcessing.mpos;
var connection = mysql.createConnection({
host: mposConfig.host,
port: mposConfig.port,
user: mposConfig.user,
password: mposConfig.password,
database: mposConfig.database
});
connection.connect(function(err){
logger.logError('shareProcessor', 'database', config.coin.name +
' - could not connect to mysql database: ' + JSON.stringify(err))
});
connection.on('error', function(err){
logger.logError('shareProcessor', 'database', config.coin.name +
' - mysql database error: ' + JSON.stringify(err))
});
var insertShare = function(isValidShare, isValidBlock, data){
connection.query(
'INSERT INTO `shares` SET time = NOW(), rem_host = ?, username = ?, our_result = ?, upstream_result = ?, difficulty = ?, reason = ?, solution = ?',
[data.ip, data.worker, isValidShare ? 'Y' : 'N', isValidBlock ? 'Y' : 'N', data.difficulty, data.error, data.solution],
function(err, result) {
if (err)
logger.logError('shareProcessor', 'database', 'MySQL insert error when adding share: ' +
JSON.stringify(err));
}
);
};
var updateDifficulty = function(workerName, diff){
connection.query(
'UPDATE `pool_worker` SET `difficulty` = ' + diff + ' WHERE `username` = ' + connection.escape(workerName),
function(err, result){
if (err)
logger.logError('shareProcessor', 'database', 'MySQL error when updating worker diff: ' +
JSON.stringify(err));
else if (result.affectedRows === 0){
connection.query('INSERT INTO `pool_worker` SET ?', {username: workerName, difficulty: diff});
}
else
console.log('Updated difficulty successfully', result);
}
);
};
handlers[config.coin.name] = {insertShare: insertShare, updateDifficulty: updateDifficulty};
});
return handlers;
})();
function handleShare(data){ function handleShare(data){
var shareData = data.share; var shareData = data.share;
var coin = data.coin; var coin = data.coin;
client.hincrby([coin + ':' + shareData.height, shareData.worker, shareData.difficulty], function(error, result){ var poolConfig = poolConfigs[coin];
if (error)
logger.logError('shareProcessor', 'database', 'could not store worker share') if (poolConfig.shareProcessing.mpos && poolConfig.shareProcessing.mpos.enabled){
}); poolMposHandlers[coin].insertShare(data.isValidShare, data.isValidBlock, shareData);
}
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', 'database', 'could not store worker share')
});
}
} }
function handleBlock(data){ function handleBlock(data){

View File

@ -8,7 +8,8 @@
"dateformat": "*", "dateformat": "*",
"node-json-minify": "*", "node-json-minify": "*",
"posix": "*", "posix": "*",
"redis": "*" "redis": "*",
"mysql": "felixge/node-mysql",
}, },
"devDependencies": {}, "devDependencies": {},
"scripts": { "scripts": {

View File

@ -6,8 +6,9 @@
"enabled": false, "enabled": false,
"host": "localhost", "host": "localhost",
"port": 3306, "port": 3306,
"name": "doge", "user": "me",
"password": "mypass" "password": "mypass",
"database": "ltc"
}, },
"internal": { "internal": {
"enabled": true, "enabled": true,