Merge pull request #109 from hellcatz/patch-2
Major Updates 2 ```major: added support for komodo, zcash, zclassic, (zdash not tested) coins: added requireShielding boolean coins: updated zcash_testnet founders addresses configs: added komodo_example.conf api.js: added payments json api call stats.js: improved getTotalSharesByAdress to support multiple coins. stats.js: report more collected stats stats.js: report payment stats stats.js: optimizations to historical data saving paymentProcessor.js: added requireShielding support paymentProcessor.js: added support to pay directly from komodo pool address without shielding first paymentProcessor.js: lower tx fee reserve for komodo to 0.00005 KMD paymentProcessor.js: tx fee reserve for all coins is 0.0004 paymentProcessor.js: improved multi-coin support paymentProcessor.js: added minConfShield and minConfPayout variables paymentProcessor.js: updated coin network stat caching paymentProcessor.js: improved operation id handling when shielding coins paymentProcessor.js: updated coin network stat caching paymentProcessor.js: updated payment stat collection in redis miner_stats.html: removed ZEC references```
This commit is contained in:
commit
05968ef296
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "komodo",
|
"name": "komodo",
|
||||||
"symbol": "kmd",
|
"symbol": "kmd",
|
||||||
"algorithm": "equihash"
|
"algorithm": "equihash",
|
||||||
|
"txfee": 0.00005
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "zcash",
|
"name": "zcash",
|
||||||
"symbol": "zec",
|
"symbol": "zec",
|
||||||
"algorithm": "equihash",
|
"algorithm": "equihash",
|
||||||
|
"requireShielding": true,
|
||||||
"payFoundersReward": true,
|
"payFoundersReward": true,
|
||||||
"percentFoundersReward": 20,
|
"percentFoundersReward": 20,
|
||||||
"maxFoundersRewardBlockHeight": 849999,
|
"maxFoundersRewardBlockHeight": 849999,
|
||||||
|
@ -56,5 +56,6 @@
|
||||||
"t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M",
|
"t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M",
|
||||||
"t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv",
|
"t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv",
|
||||||
"t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN"
|
"t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN"
|
||||||
]
|
],
|
||||||
|
"txfee": 0.0004
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "zcash_testnet",
|
"name": "zcash_testnet",
|
||||||
"symbol": "taz",
|
"symbol": "taz",
|
||||||
"algorithm": "equihash",
|
"algorithm": "equihash",
|
||||||
|
"requireShielding": true,
|
||||||
"payFoundersReward": true,
|
"payFoundersReward": true,
|
||||||
"percentFoundersReward": 20,
|
"percentFoundersReward": 20,
|
||||||
"maxFoundersRewardBlockHeight": 849999,
|
"maxFoundersRewardBlockHeight": 849999,
|
||||||
|
@ -20,5 +20,6 @@
|
||||||
"t2UjVSd3zheHPgAkuX8WQW2CiC9xHQ8EvWp", "t2TBUAhELyHUn8i6SXYsXz5Lmy7kDzA1uT5", "t2Tz3uCyhP6eizUWDc3bGH7XUC9GQsEyQNc", "t2NysJSZtLwMLWEJ6MH3BsxRh6h27mNcsSy",
|
"t2UjVSd3zheHPgAkuX8WQW2CiC9xHQ8EvWp", "t2TBUAhELyHUn8i6SXYsXz5Lmy7kDzA1uT5", "t2Tz3uCyhP6eizUWDc3bGH7XUC9GQsEyQNc", "t2NysJSZtLwMLWEJ6MH3BsxRh6h27mNcsSy",
|
||||||
"t2KXJVVyyrjVxxSeazbY9ksGyft4qsXUNm9", "t2J9YYtH31cveiLZzjaE4AcuwVho6qjTNzp", "t2QgvW4sP9zaGpPMH1GRzy7cpydmuRfB4AZ", "t2NDTJP9MosKpyFPHJmfjc5pGCvAU58XGa4",
|
"t2KXJVVyyrjVxxSeazbY9ksGyft4qsXUNm9", "t2J9YYtH31cveiLZzjaE4AcuwVho6qjTNzp", "t2QgvW4sP9zaGpPMH1GRzy7cpydmuRfB4AZ", "t2NDTJP9MosKpyFPHJmfjc5pGCvAU58XGa4",
|
||||||
"t29pHDBWq7qN4EjwSEHg8wEqYe9pkmVrtRP", "t2Ez9KM8VJLuArcxuEkNRAkhNvidKkzXcjJ", "t2D5y7J5fpXajLbGrMBQkFg2mFN8fo3n8cX", "t2UV2wr1PTaUiybpkV3FdSdGxUJeZdZztyt"
|
"t29pHDBWq7qN4EjwSEHg8wEqYe9pkmVrtRP", "t2Ez9KM8VJLuArcxuEkNRAkhNvidKkzXcjJ", "t2D5y7J5fpXajLbGrMBQkFg2mFN8fo3n8cX", "t2UV2wr1PTaUiybpkV3FdSdGxUJeZdZztyt"
|
||||||
]
|
],
|
||||||
|
"txfee": 0.0004
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
"name": "zclassic",
|
"name": "zclassic",
|
||||||
"symbol": "zcl",
|
"symbol": "zcl",
|
||||||
"algorithm": "equihash",
|
"algorithm": "equihash",
|
||||||
"peerMagic": "24e92764"
|
"requireShielding": true,
|
||||||
|
"peerMagic": "24e92764",
|
||||||
|
"txfee": 0.0004
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "zdash",
|
"name": "zdash",
|
||||||
"symbol": "zdash",
|
"symbol": "zdash",
|
||||||
"algorithm": "equihash"
|
"algorithm": "equihash",
|
||||||
|
"requireShielding": true,
|
||||||
|
"txfee": 0.0004
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"defaultPoolConfigs": {
|
"defaultPoolConfigs": {
|
||||||
"blockRefreshInterval": 30,
|
"blockRefreshInterval": 500,
|
||||||
"jobRebroadcastTimeout": 55,
|
"jobRebroadcastTimeout": 55,
|
||||||
"connectionTimeout": 600,
|
"connectionTimeout": 600,
|
||||||
"emitInvalidBlockHashes": false,
|
"emitInvalidBlockHashes": false,
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"stratumHost": "cryppit.com",
|
"stratumHost": "cryppit.com",
|
||||||
"stats": {
|
"stats": {
|
||||||
"updateInterval": 30,
|
"updateInterval": 30,
|
||||||
"historicalRetention": 43200,
|
"historicalRetention": 14400,
|
||||||
"hashrateWindow": 300
|
"hashrateWindow": 300
|
||||||
},
|
},
|
||||||
"adminCenter": {
|
"adminCenter": {
|
||||||
|
|
|
@ -19,6 +19,13 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
return;
|
return;
|
||||||
case 'pool_stats':
|
case 'pool_stats':
|
||||||
res.end(JSON.stringify(portalStats.statPoolHistory));
|
res.end(JSON.stringify(portalStats.statPoolHistory));
|
||||||
|
return;
|
||||||
|
case 'payments':
|
||||||
|
var poolBlocks = [];
|
||||||
|
for(var pool in portalStats.stats.pools) {
|
||||||
|
poolBlocks.push({name: pool, pending: portalStats.stats.pools[pool].pending, payments: portalStats.stats.pools[pool].payments});
|
||||||
|
}
|
||||||
|
res.end(JSON.stringify(poolBlocks));
|
||||||
return;
|
return;
|
||||||
case 'worker_stats':
|
case 'worker_stats':
|
||||||
if (req.url.indexOf("?")>0) {
|
if (req.url.indexOf("?")>0) {
|
||||||
|
@ -56,7 +63,6 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
//console.log(portalStats.statHistory[h].time);
|
//console.log(portalStats.statHistory[h].time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// note, h is the last record from above loop, which is latest
|
|
||||||
for(var pool in portalStats.stats.pools) {
|
for(var pool in portalStats.stats.pools) {
|
||||||
for(var w in portalStats.stats.pools[pool].workers){
|
for(var w in portalStats.stats.pools[pool].workers){
|
||||||
if (w.startsWith(address)) {
|
if (w.startsWith(address)) {
|
||||||
|
|
|
@ -51,6 +51,14 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
var logComponent = coin;
|
var logComponent = coin;
|
||||||
var opidCount = 0;
|
var opidCount = 0;
|
||||||
|
|
||||||
|
var minConfShield = 3;
|
||||||
|
var minConfPayout = 10;
|
||||||
|
|
||||||
|
var requireShielding = poolOptions.coin.requireShielding === true;
|
||||||
|
var fee = parseFloat(poolOptions.coin.txfee) || parseFloat(0.0004);
|
||||||
|
|
||||||
|
logger.special(logSystem, logComponent, logComponent + ' requireShielding: ' + requireShielding);
|
||||||
|
|
||||||
var daemon = new Stratum.daemon.interface([processingConfig.daemon], function(severity, message){
|
var daemon = new Stratum.daemon.interface([processingConfig.daemon], function(severity, message){
|
||||||
logger[severity](logSystem, logComponent, message);
|
logger[severity](logSystem, logComponent, message);
|
||||||
});
|
});
|
||||||
|
@ -279,44 +287,51 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cacheZCashNetworkStats () {
|
|
||||||
|
function cacheNetworkStats () {
|
||||||
var params = null;
|
var params = null;
|
||||||
daemon.cmd('getmininginfo', params,
|
daemon.cmd('getmininginfo', params,
|
||||||
function (result) {
|
function (result) {
|
||||||
if (result.error) {
|
|
||||||
logger.error(logSystem, logComponent, 'Error getting stats from zcashd'
|
|
||||||
+ JSON.stringify(result.error));
|
|
||||||
} else {
|
|
||||||
logger.special(logSystem, logComponent, "Updating "+logComponent+" network stats...");
|
|
||||||
var coin = logComponent;
|
|
||||||
var finalRedisCommands = [];
|
var finalRedisCommands = [];
|
||||||
|
var coin = logComponent;
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
logger.error(logSystem, logComponent, 'Error with RPC call `getmininginfo`'
|
||||||
|
+ JSON.stringify(result.error));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (result[0].response.blocks !== null) {
|
||||||
finalRedisCommands.push(['hset', coin + ':stats', 'networkBlocks', result[0].response.blocks]);
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkBlocks', result[0].response.blocks]);
|
||||||
finalRedisCommands.push(['hset', coin + ':stats', 'networkDiff', result[0].response.difficulty]);
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkDiff', result[0].response.difficulty]);
|
||||||
finalRedisCommands.push(['hset', coin + ':stats', 'networkSols', result[0].response.networksolps]);
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkSols', result[0].response.networksolps]);
|
||||||
redisClient.multi(finalRedisCommands).exec(function(error, results){
|
} else {
|
||||||
if (error){
|
logger.error(logSystem, logComponent, "Error parse RPC call reponse.blocks tp `getmininginfo`." + JSON.stringify(result[0].response));
|
||||||
logger.error(logSystem, logComponent, 'Could not update zcash stats to redis ' + JSON.stringify(error));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
daemon.cmd('getinfo', params,
|
|
||||||
|
daemon.cmd('getnetworkinfo', params,
|
||||||
function (result) {
|
function (result) {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
logger.error(logSystem, logComponent, 'Error getting stats from zcashd'
|
logger.error(logSystem, logComponent, 'Error with RPC call `getnetworkinfo`'
|
||||||
+ JSON.stringify(result.error));
|
+ JSON.stringify(result.error));
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
var coin = logComponent;
|
if (result[0].response !== null) {
|
||||||
var finalRedisCommands = [];
|
|
||||||
finalRedisCommands.push(['hset', coin + ':stats', 'networkConnections', result[0].response.connections]);
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkConnections', result[0].response.connections]);
|
||||||
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkVersion', result[0].response.version]);
|
||||||
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkSubVersion', result[0].response.subversion]);
|
||||||
|
finalRedisCommands.push(['hset', coin + ':stats', 'networkProtocolVersion', result[0].response.protocolversion]);
|
||||||
|
} else {
|
||||||
|
logger.error(logSystem, logComponent, "Error parse RPC call response to `getnetworkinfo`." + JSON.stringify(result[0].response));
|
||||||
|
}
|
||||||
|
}
|
||||||
redisClient.multi(finalRedisCommands).exec(function(error, results){
|
redisClient.multi(finalRedisCommands).exec(function(error, results){
|
||||||
if (error){
|
if (error){
|
||||||
logger.error(logSystem, logComponent, 'Could not update zcash stats to redis ' + JSON.stringify(error));
|
logger.error(logSystem, logComponent, 'Error update coin stats to redis ' + JSON.stringify(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -326,23 +341,27 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
var intervalState = 0; // do not send ZtoT and TtoZ and same time, this results in operation failed!
|
var intervalState = 0; // do not send ZtoT and TtoZ and same time, this results in operation failed!
|
||||||
var interval = poolOptions.walletInterval * 60 * 1000; // run every x minutes
|
var interval = poolOptions.walletInterval * 60 * 1000; // run every x minutes
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
|
// shielding not required for some equihash coins
|
||||||
|
if (requireShielding === true) {
|
||||||
intervalState++;
|
intervalState++;
|
||||||
switch (intervalState){
|
switch (intervalState) {
|
||||||
case 1:
|
case 1:
|
||||||
listUnspent(poolOptions.address, null, 1, false, sendTToZ);
|
listUnspent(poolOptions.address, null, minConfShield, false, sendTToZ);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
listUnspentZ(poolOptions.zAddress, 1, false, sendZToT);
|
listUnspentZ(poolOptions.zAddress, minConfShield, false, sendZToT);
|
||||||
//listUnspent(null, poolOptions.address, 1, true, function (){});
|
|
||||||
intervalState = 0;
|
intervalState = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// update zcash stats
|
}
|
||||||
cacheZCashNetworkStats();
|
// update network stats using coin daemon
|
||||||
|
cacheNetworkStats();
|
||||||
}, interval);
|
}, interval);
|
||||||
|
|
||||||
// check operation statuses every x seconds
|
// check operation statuses every x seconds
|
||||||
var opid_interval = poolOptions.walletInterval * 1000;
|
var opid_interval = poolOptions.walletInterval * 1000;
|
||||||
|
// shielding not required for some equihash coins
|
||||||
|
if (requireShielding === true) {
|
||||||
setInterval(function(){
|
setInterval(function(){
|
||||||
var checkOpIdSuccessAndGetResult = function(ops) {
|
var checkOpIdSuccessAndGetResult = function(ops) {
|
||||||
ops.forEach(function(op, i){
|
ops.forEach(function(op, i){
|
||||||
|
@ -383,14 +402,14 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
}
|
}
|
||||||
}, true, true);
|
}, true, true);
|
||||||
}, opid_interval);
|
}, opid_interval);
|
||||||
|
}
|
||||||
|
|
||||||
var satoshisToCoins = function(satoshis){
|
var satoshisToCoins = function(satoshis){
|
||||||
return parseFloat((satoshis / magnitude).toFixed(coinPrecision));
|
return parseFloat((satoshis / magnitude).toFixed(coinPrecision));
|
||||||
};
|
};
|
||||||
|
|
||||||
var coinsToSatoshies = function(coins){
|
var coinsToSatoshies = function(coins){
|
||||||
return coins * magnitude;
|
return Math.round(coins * magnitude);
|
||||||
};
|
};
|
||||||
|
|
||||||
function balanceRound(number) {
|
function balanceRound(number) {
|
||||||
|
@ -478,7 +497,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
}
|
}
|
||||||
|
|
||||||
// update confirmations in redis for pending blocks
|
// update confirmations in redis for pending blocks
|
||||||
var confirmsUpdate = blockDetails.map(function(b){
|
var confirmsUpdate = blockDetails.map(function(b) {
|
||||||
if (b.result != null && b.result.confirmations > 0) {
|
if (b.result != null && b.result.confirmations > 0) {
|
||||||
if (b.result.confirmations > 100) {
|
if (b.result.confirmations > 100) {
|
||||||
return ['hdel', logComponent + ':blocksPendingConfirms', b.result.hash];
|
return ['hdel', logComponent + ':blocksPendingConfirms', b.result.hash];
|
||||||
|
@ -612,7 +631,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
|
|
||||||
round.category = generationTx.category;
|
round.category = generationTx.category;
|
||||||
if (round.category === 'generate') {
|
if (round.category === 'generate') {
|
||||||
round.reward = generationTx.amount - 0.0004 || generationTx.value - 0.0004; // TODO: Adjust fees to be dynamic
|
round.reward = balanceRound(generationTx.amount - fee) || balanceRound(generationTx.value - fee); // TODO: Adjust fees to be dynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -643,18 +662,28 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// check if we have enough tAddress funds to send payments
|
var feeSatoshi = fee * magnitude;
|
||||||
var totalOwed = 0;
|
|
||||||
|
// calculate what the pool owes its miners
|
||||||
|
var totalOwed = parseInt(0);
|
||||||
for (var i = 0; i < rounds.length; i++) {
|
for (var i = 0; i < rounds.length; i++) {
|
||||||
totalOwed = totalOwed + (rounds[i].reward * magnitude) - 4000; // TODO: make tx fees dynamic
|
totalOwed = totalOwed + Math.round(rounds[i].reward * magnitude) - feeSatoshi; // TODO: make tx fees dynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
listUnspent(null, poolOptions.address, 1, false, function (error, tBalance){
|
var notAddr = null;
|
||||||
if (tBalance < totalOwed) {
|
if (requireShielding === true) {
|
||||||
logger.error(logSystem, logComponent, (tBalance / magnitude).toFixed(8) + ' is not enough payment funds to process ' + (totalOwed / magnitude).toFixed(8) + ' of payments. (Possibly due to pending txs)');
|
notAddr = poolOptions.address;
|
||||||
return callback(true);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
// check if we have enough tAddress funds to brgin payment processing
|
||||||
|
listUnspent(null, notAddr, minConfPayout, false, function (error, tBalance){
|
||||||
|
if (error) {
|
||||||
|
logger.error(logSystem, logComponent, 'Error checking pool balance before payouts. (Unable to begin payment process)');
|
||||||
|
return callback(true);
|
||||||
|
} else if (tBalance < totalOwed) {
|
||||||
|
logger.error(logSystem, logComponent, 'Insufficient pool funds to being payment process; '+(tBalance / magnitude).toFixed(8) + ' < ' + (totalOwed / magnitude).toFixed(8)+'. (Possibly due to pending txs) ');
|
||||||
|
return callback(true);
|
||||||
|
} else {
|
||||||
// zcash daemon does not support account feature
|
// zcash daemon does not support account feature
|
||||||
addressAccount = "";
|
addressAccount = "";
|
||||||
callback(null, workers, rounds, addressAccount);
|
callback(null, workers, rounds, addressAccount);
|
||||||
|
@ -851,7 +880,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
return parseInt(r.height);
|
return parseInt(r.height);
|
||||||
});
|
});
|
||||||
var paymentsUpdate = [];
|
var paymentsUpdate = [];
|
||||||
var paymentsData = [{txid:txid, paid:balanceRound(totalSent / magnitude), shares:totalShares, miners:Object.keys(addressAmounts).length}, {blocks: paymentBlocks}, addressAmounts];
|
var paymentsData = {time:Date.now(), txid:txid, shares:totalShares, paid:balanceRound(totalSent / magnitude), miners:Object.keys(addressAmounts).length, blocks: paymentBlocks, amounts: addressAmounts};
|
||||||
paymentsUpdate.push(['zadd', logComponent + ':payments', Date.now(), JSON.stringify(paymentsData)]);
|
paymentsUpdate.push(['zadd', logComponent + ':payments', Date.now(), JSON.stringify(paymentsData)]);
|
||||||
startRedisTimer();
|
startRedisTimer();
|
||||||
redisClient.multi(paymentsUpdate).exec(function(error, payments){
|
redisClient.multi(paymentsUpdate).exec(function(error, payments){
|
||||||
|
@ -882,11 +911,12 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
},
|
},
|
||||||
function(workers, rounds, callback){
|
function(workers, rounds, callback){
|
||||||
|
|
||||||
var totalPaid = 0;
|
var totalPaid = parseFloat(0);
|
||||||
|
|
||||||
var balanceUpdateCommands = [];
|
var balanceUpdateCommands = [];
|
||||||
var workerPayoutsCommand = [];
|
var workerPayoutsCommand = [];
|
||||||
|
|
||||||
|
// update worker paid/balance stats
|
||||||
for (var w in workers) {
|
for (var w in workers) {
|
||||||
var worker = workers[w];
|
var worker = workers[w];
|
||||||
if (worker.balanceChange !== 0){
|
if (worker.balanceChange !== 0){
|
||||||
|
@ -916,6 +946,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// handle the round
|
||||||
rounds.forEach(function(r){
|
rounds.forEach(function(r){
|
||||||
switch(r.category){
|
switch(r.category){
|
||||||
case 'kicked':
|
case 'kicked':
|
||||||
|
@ -994,5 +1025,4 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||||
else return address;
|
else return address;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ var zlib = require('zlib');
|
||||||
|
|
||||||
var redis = require('redis');
|
var redis = require('redis');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
//var fancyTimestamp = require('fancy-timestamp');
|
|
||||||
|
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
|
|
||||||
|
@ -176,11 +175,18 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
var client = redisClients[0].client,
|
var client = redisClients[0].client,
|
||||||
coins = redisClients[0].coins,
|
coins = redisClients[0].coins,
|
||||||
shares = [];
|
shares = [];
|
||||||
var totalShares = 0;
|
|
||||||
|
var pindex = parseInt(0);
|
||||||
|
var totalShares = parseFloat(0);
|
||||||
async.each(_this.stats.pools, function(pool, pcb) {
|
async.each(_this.stats.pools, function(pool, pcb) {
|
||||||
|
pindex++;
|
||||||
var coin = String(_this.stats.pools[pool.name].name);
|
var coin = String(_this.stats.pools[pool.name].name);
|
||||||
client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", function(error, result) {
|
client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", function(error, result) {
|
||||||
var workerName = "";
|
if (error) {
|
||||||
|
pcb(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var workerName="";
|
||||||
var shares = 0;
|
var shares = 0;
|
||||||
for (var i in result[1]) {
|
for (var i in result[1]) {
|
||||||
if (Math.abs(i % 2) != 1) {
|
if (Math.abs(i % 2) != 1) {
|
||||||
|
@ -189,7 +195,9 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
shares += parseFloat(result[1][i]);
|
shares += parseFloat(result[1][i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (shares>0) {
|
||||||
totalShares = shares;
|
totalShares = shares;
|
||||||
|
}
|
||||||
pcb();
|
pcb();
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
|
@ -197,7 +205,10 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
cback(0);
|
cback(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (totalShares > 0 || (pindex >= Object.keys(_this.stats.pools).length)) {
|
||||||
cback(totalShares);
|
cback(totalShares);
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -285,7 +296,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
['smembers', ':blocksConfirmed'],
|
['smembers', ':blocksConfirmed'],
|
||||||
['hgetall', ':shares:roundCurrent'],
|
['hgetall', ':shares:roundCurrent'],
|
||||||
['hgetall', ':blocksPendingConfirms'],
|
['hgetall', ':blocksPendingConfirms'],
|
||||||
['hgetall', ':payments']
|
['zrange', ':payments', -100, -1]
|
||||||
];
|
];
|
||||||
|
|
||||||
var commandsPerCoin = redisCommandTemplates.length;
|
var commandsPerCoin = redisCommandTemplates.length;
|
||||||
|
@ -320,23 +331,39 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
networkSols: replies[i + 2] ? (replies[i + 2].networkSols || 0) : 0,
|
networkSols: replies[i + 2] ? (replies[i + 2].networkSols || 0) : 0,
|
||||||
networkSolsString: getReadableNetworkHashRateString(replies[i + 2] ? (replies[i + 2].networkSols || 0) : 0),
|
networkSolsString: getReadableNetworkHashRateString(replies[i + 2] ? (replies[i + 2].networkSols || 0) : 0),
|
||||||
networkDiff: replies[i + 2] ? (replies[i + 2].networkDiff || 0) : 0,
|
networkDiff: replies[i + 2] ? (replies[i + 2].networkDiff || 0) : 0,
|
||||||
networkConnections: replies[i + 2] ? (replies[i + 2].networkConnections || 0) : 0
|
networkConnections: replies[i + 2] ? (replies[i + 2].networkConnections || 0) : 0,
|
||||||
|
networkVersion: replies[i + 2] ? (replies[i + 2].networkSubVersion || 0) : 0,
|
||||||
|
networkProtocolVersion: replies[i + 2] ? (replies[i + 2].networkProtocolVersion || 0) : 0
|
||||||
},
|
},
|
||||||
|
/* block stat counts */
|
||||||
blocks: {
|
blocks: {
|
||||||
pending: replies[i + 3],
|
pending: replies[i + 3],
|
||||||
confirmed: replies[i + 4],
|
confirmed: replies[i + 4],
|
||||||
orphaned: replies[i + 5]
|
orphaned: replies[i + 5]
|
||||||
},
|
},
|
||||||
|
/* show all pending blocks */
|
||||||
pending: {
|
pending: {
|
||||||
blocks: replies[i + 6].sort(sortBlocks),
|
blocks: replies[i + 6].sort(sortBlocks),
|
||||||
confirms: replies[i + 9]
|
confirms: replies[i + 9]
|
||||||
},
|
},
|
||||||
|
/* show last 5 found blocks */
|
||||||
confirmed: {
|
confirmed: {
|
||||||
blocks: replies[i + 7].sort(sortBlocks)
|
blocks: replies[i + 7].sort(sortBlocks).slice(0,5)
|
||||||
},
|
},
|
||||||
payments: replies[i + 10],
|
payments: [],
|
||||||
currentRoundShares: replies[i + 8]
|
currentRoundShares: replies[i + 8]
|
||||||
};
|
};
|
||||||
|
for(var j = replies[i + 10].length; j > 0; j--){
|
||||||
|
var jsonObj;
|
||||||
|
try {
|
||||||
|
jsonObj = JSON.parse(replies[i + 10][j-1]);
|
||||||
|
} catch(e) {
|
||||||
|
jsonObj = null;
|
||||||
|
}
|
||||||
|
if (jsonObj !== null) {
|
||||||
|
coinStats.payments.push(jsonObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
for (var b in coinStats.confirmed.blocks) {
|
for (var b in coinStats.confirmed.blocks) {
|
||||||
var parms = coinStats.confirmed.blocks[b].split(':');
|
var parms = coinStats.confirmed.blocks[b].split(':');
|
||||||
|
@ -460,7 +487,8 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
var shareMultiplier = Math.pow(2, 32) / algos[coinStats.algorithm].multiplier;
|
var shareMultiplier = Math.pow(2, 32) / algos[coinStats.algorithm].multiplier;
|
||||||
coinStats.hashrate = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow;
|
coinStats.hashrate = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow;
|
||||||
coinStats.hashrateString = _this.getReadableHashRateString(coinStats.hashrate);
|
coinStats.hashrateString = _this.getReadableHashRateString(coinStats.hashrate);
|
||||||
var _blocktime = 250;
|
|
||||||
|
var _blocktime = 160;
|
||||||
var _networkHashRate = parseFloat(coinStats.poolStats.networkSols) * 1.2;
|
var _networkHashRate = parseFloat(coinStats.poolStats.networkSols) * 1.2;
|
||||||
var _myHashRate = (coinStats.hashrate / 1000000) * 2;
|
var _myHashRate = (coinStats.hashrate / 1000000) * 2;
|
||||||
coinStats.luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
|
coinStats.luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
|
||||||
|
@ -481,7 +509,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
portalStats.algos[algo].hashrate += coinStats.hashrate;
|
portalStats.algos[algo].hashrate += coinStats.hashrate;
|
||||||
portalStats.algos[algo].workers += Object.keys(coinStats.workers).length;
|
portalStats.algos[algo].workers += Object.keys(coinStats.workers).length;
|
||||||
|
|
||||||
var _shareTotal = 0;
|
var _shareTotal = parseFloat(0);
|
||||||
for (var worker in coinStats.currentRoundShares) {
|
for (var worker in coinStats.currentRoundShares) {
|
||||||
var miner = worker.split(".")[0];
|
var miner = worker.split(".")[0];
|
||||||
if (miner in coinStats.miners) {
|
if (miner in coinStats.miners) {
|
||||||
|
@ -492,10 +520,9 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
}
|
}
|
||||||
_shareTotal += parseFloat(coinStats.currentRoundShares[worker]);
|
_shareTotal += parseFloat(coinStats.currentRoundShares[worker]);
|
||||||
}
|
}
|
||||||
coinStats.shareCount = Math.round(_shareTotal * 100) / 100;
|
coinStats.shareCount = _shareTotal;
|
||||||
|
|
||||||
for (var worker in coinStats.workers) {
|
for (var worker in coinStats.workers) {
|
||||||
var _blocktime = 250;
|
|
||||||
var _workerRate = shareMultiplier * coinStats.workers[worker].shares / portalConfig.website.stats.hashrateWindow;
|
var _workerRate = shareMultiplier * coinStats.workers[worker].shares / portalConfig.website.stats.hashrateWindow;
|
||||||
var _wHashRate = (_workerRate / 1000000) * 2;
|
var _wHashRate = (_workerRate / 1000000) * 2;
|
||||||
coinStats.workers[worker].luckDays = ((_networkHashRate / _wHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
|
coinStats.workers[worker].luckDays = ((_networkHashRate / _wHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
|
||||||
|
@ -504,7 +531,6 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate);
|
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate);
|
||||||
}
|
}
|
||||||
for (var miner in coinStats.miners) {
|
for (var miner in coinStats.miners) {
|
||||||
var _blocktime = 250;
|
|
||||||
var _workerRate = shareMultiplier * coinStats.miners[miner].shares / portalConfig.website.stats.hashrateWindow;
|
var _workerRate = shareMultiplier * coinStats.miners[miner].shares / portalConfig.website.stats.hashrateWindow;
|
||||||
var _wHashRate = (_workerRate / 1000000) * 2;
|
var _wHashRate = (_workerRate / 1000000) * 2;
|
||||||
coinStats.miners[miner].luckDays = ((_networkHashRate / _wHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
|
coinStats.miners[miner].luckDays = ((_networkHashRate / _wHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
|
||||||
|
@ -525,12 +551,19 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
algoStats.hashrateString = _this.getReadableHashRateString(algoStats.hashrate);
|
algoStats.hashrateString = _this.getReadableHashRateString(algoStats.hashrate);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO, create stats object and copy elements from portalStats we want to display...
|
|
||||||
var showStats = portalStats;
|
|
||||||
|
|
||||||
_this.stats = portalStats;
|
_this.stats = portalStats;
|
||||||
_this.statsString = JSON.stringify(portalStats);
|
|
||||||
_this.statHistory.push(portalStats);
|
// save historical hashrate, not entire stats!
|
||||||
|
var saveStats = JSON.parse(JSON.stringify(portalStats));
|
||||||
|
Object.keys(saveStats.pools).forEach(function(pool){
|
||||||
|
delete saveStats.pools[pool].pending;
|
||||||
|
delete saveStats.pools[pool].confirmed;
|
||||||
|
delete saveStats.pools[pool].currentRoundShares;
|
||||||
|
delete saveStats.pools[pool].payments;
|
||||||
|
delete saveStats.pools[pool].miners;
|
||||||
|
});
|
||||||
|
_this.statsString = JSON.stringify(saveStats);
|
||||||
|
_this.statHistory.push(saveStats);
|
||||||
|
|
||||||
addStatPoolHistory(portalStats);
|
addStatPoolHistory(portalStats);
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,6 @@ module.exports = function(logger){
|
||||||
|
|
||||||
var payout = function(req, res, next){
|
var payout = function(req, res, next){
|
||||||
var address = req.params.address || null;
|
var address = req.params.address || null;
|
||||||
|
|
||||||
if (address != null){
|
if (address != null){
|
||||||
portalStats.getPayout(address, function(data){
|
portalStats.getPayout(address, function(data){
|
||||||
res.write(data.toString());
|
res.write(data.toString());
|
||||||
|
@ -241,26 +240,20 @@ module.exports = function(logger){
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var shares = function(req, res, next){
|
var shares = function(req, res, next){
|
||||||
portalStats.getCoins(function(){
|
portalStats.getCoins(function(){
|
||||||
processTemplates();
|
processTemplates();
|
||||||
|
|
||||||
res.end(indexesProcessed['user_shares']);
|
res.end(indexesProcessed['user_shares']);
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var usershares = function(req, res, next){
|
var usershares = function(req, res, next){
|
||||||
|
|
||||||
var coin = req.params.coin || null;
|
var coin = req.params.coin || null;
|
||||||
|
|
||||||
if(coin != null){
|
if(coin != null){
|
||||||
portalStats.getCoinTotals(coin, null, function(){
|
portalStats.getCoinTotals(coin, null, function(){
|
||||||
processTemplates();
|
processTemplates();
|
||||||
|
|
||||||
res.end(indexesProcessed['user_shares']);
|
res.end(indexesProcessed['user_shares']);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
"enabled":false,
|
||||||
|
"coin": "komodo.json",
|
||||||
|
|
||||||
|
"address": "",
|
||||||
|
"_comment_address": "pools komodo address; ex, RSXGTHQSqwcMw1vowKfEE7sQ8fAmv1tmso",
|
||||||
|
|
||||||
|
"zAddress": "",
|
||||||
|
"_comment_zAddress": "shielding not required in komodo, not used",
|
||||||
|
|
||||||
|
"tAddress": "",
|
||||||
|
"_comment_tAddress": "set to same as pools komodo address; ex, RSXGTHQSqwcMw1vowKfEE7sQ8fAmv1tmso",
|
||||||
|
|
||||||
|
"walletInterval": 1,
|
||||||
|
"_comment_walletInterval": "Used to cache komodo coin stats, shielding not performed.",
|
||||||
|
|
||||||
|
"rewardRecipients": {
|
||||||
|
"": 1.0
|
||||||
|
},
|
||||||
|
|
||||||
|
"tlsOptions": {
|
||||||
|
"enabled": false,
|
||||||
|
"serverKey":"",
|
||||||
|
"serverCert":"",
|
||||||
|
"ca":""
|
||||||
|
},
|
||||||
|
|
||||||
|
"paymentProcessing": {
|
||||||
|
"enabled": true,
|
||||||
|
"paymentInterval": 57,
|
||||||
|
"_comment_paymentInterval": "Interval in seconds to check and perform payments.",
|
||||||
|
"minimumPayment": 0.1,
|
||||||
|
"daemon": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8232,
|
||||||
|
"user": "username",
|
||||||
|
"password": "password"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ports": {
|
||||||
|
"3857": {
|
||||||
|
"tls":false,
|
||||||
|
"diff": 0.05,
|
||||||
|
"varDiff": {
|
||||||
|
"minDiff": 0.04,
|
||||||
|
"maxDiff": 16,
|
||||||
|
"targetTime": 15,
|
||||||
|
"retargetTime": 60,
|
||||||
|
"variancePercent": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"daemons": [
|
||||||
|
{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8232,
|
||||||
|
"user": "username",
|
||||||
|
"password": "password"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"p2p": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 19333,
|
||||||
|
"disableTransactions": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"mposMode": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 3306,
|
||||||
|
"user": "me",
|
||||||
|
"password": "mypass",
|
||||||
|
"database": "kmd",
|
||||||
|
"checkPassword": true,
|
||||||
|
"autoCreateWorker": false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/api/stats">/stats</a> global pool stats</li>
|
<li><a href="/api/stats">/stats</a> global pool stats</li>
|
||||||
<li><a href="/api/pool_stats">/pool_stats</a> - historical stats</li>
|
<li><a href="/api/pool_stats">/pool_stats</a> - historical stats</li>
|
||||||
|
<li><a href="/api/payments">/payments</a> - payment history</li>
|
||||||
<li><a href="/api/worker_stats?taddr">/worker_stats?taddr</a> - historical time per pool json </li>
|
<li><a href="/api/worker_stats?taddr">/worker_stats?taddr</a> - historical time per pool json </li>
|
||||||
<li><a href="/api/live_stats">/live_stats</a> - live stats </li>
|
<li><a href="/api/live_stats">/live_stats</a> - live stats </li>
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@
|
||||||
<div class="chartHolder"><svg id="workerHashrate" /></div>
|
<div class="chartHolder"><svg id="workerHashrate" /></div>
|
||||||
<div>
|
<div>
|
||||||
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fa fa-cog"></i> Shares: <span id="statsTotalShares">...</span></div>
|
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fa fa-cog"></i> Shares: <span id="statsTotalShares">...</span></div>
|
||||||
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Bal: <span id="statsTotalBal">...</span> ZEC </div>
|
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Bal: <span id="statsTotalBal">...</span> </div>
|
||||||
<div style="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</span> ZEC </div>
|
<div style="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</span> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue