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:
hellcatz 2017-04-01 17:04:04 -07:00
parent 24f48be37c
commit c04bcf166d
9 changed files with 289 additions and 142 deletions

View File

@ -2,7 +2,7 @@
"name": "zcash",
"symbol": "zec",
"algorithm": "equihash",
"requireShielding": true,
"payFoundersReward": true,
"percentFoundersReward": 20,
"maxFoundersRewardBlockHeight": 849999,

View File

@ -2,23 +2,23 @@
"name": "zcash_testnet",
"symbol": "taz",
"algorithm": "equihash",
"requireShielding": true,
"payFoundersReward": true,
"percentFoundersReward": 20,
"maxFoundersRewardBlockHeight": 849999,
"foundersRewardAddressChangeInterval": 17709.3125,
"vFoundersRewardAddress": [
"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543", "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2", "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy",
"t2BkYdVCHzvTJJUTx4yZB8qeegD8QsPx8bo", "t2J8q1xH1EuigJ52MfExyyjYtN3VgvshKDf", "t2Crq9mydTm37kZokC68HzT6yez3t2FBnFj", "t2EaMPUiQ1kthqcP5UEkF42CAFKJqXCkXC9",
"t2F9dtQc63JDDyrhnfpzvVYTJcr57MkqA12", "t2LPirmnfYSZc481GgZBa6xUGcoovfytBnC", "t26xfxoSw2UV9Pe5o3C8V4YybQD4SESfxtp", "t2D3k4fNdErd66YxtvXEdft9xuLoKD7CcVo",
"t2DWYBkxKNivdmsMiivNJzutaQGqmoRjRnL", "t2C3kFF9iQRxfc4B9zgbWo4dQLLqzqjpuGQ", "t2MnT5tzu9HSKcppRyUNwoTp8MUueuSGNaB", "t2AREsWdoW1F8EQYsScsjkgqobmgrkKeUkK",
"t2Vf4wKcJ3ZFtLj4jezUUKkwYR92BLHn5UT", "t2K3fdViH6R5tRuXLphKyoYXyZhyWGghDNY", "t2VEn3KiKyHSGyzd3nDw6ESWtaCQHwuv9WC", "t2F8XouqdNMq6zzEvxQXHV1TjwZRHwRg8gC",
"t2BS7Mrbaef3fA4xrmkvDisFVXVrRBnZ6Qj", "t2FuSwoLCdBVPwdZuYoHrEzxAb9qy4qjbnL", "t2SX3U8NtrT6gz5Db1AtQCSGjrpptr8JC6h", "t2V51gZNSoJ5kRL74bf9YTtbZuv8Fcqx2FH",
"t2FyTsLjjdm4jeVwir4xzj7FAkUidbr1b4R", "t2EYbGLekmpqHyn8UBF6kqpahrYm7D6N1Le", "t2NQTrStZHtJECNFT3dUBLYA9AErxPCmkka", "t2GSWZZJzoesYxfPTWXkFn5UaxjiYxGBU2a",
"t2RpffkzyLRevGM3w9aWdqMX6bd8uuAK3vn", "t2JzjoQqnuXtTGSN7k7yk5keURBGvYofh1d", "t2AEefc72ieTnsXKmgK2bZNckiwvZe3oPNL", "t2NNs3ZGZFsNj2wvmVd8BSwSfvETgiLrD8J",
"t2ECCQPVcxUCSSQopdNquguEPE14HsVfcUn", "t2JabDUkG8TaqVKYfqDJ3rqkVdHKp6hwXvG", "t2FGzW5Zdc8Cy98ZKmRygsVGi6oKcmYir9n", "t2DUD8a21FtEFn42oVLp5NGbogY13uyjy9t",
"t2UjVSd3zheHPgAkuX8WQW2CiC9xHQ8EvWp", "t2TBUAhELyHUn8i6SXYsXz5Lmy7kDzA1uT5", "t2Tz3uCyhP6eizUWDc3bGH7XUC9GQsEyQNc", "t2NysJSZtLwMLWEJ6MH3BsxRh6h27mNcsSy",
"t2KXJVVyyrjVxxSeazbY9ksGyft4qsXUNm9", "t2J9YYtH31cveiLZzjaE4AcuwVho6qjTNzp", "t2QgvW4sP9zaGpPMH1GRzy7cpydmuRfB4AZ", "t2NDTJP9MosKpyFPHJmfjc5pGCvAU58XGa4",
"t29pHDBWq7qN4EjwSEHg8wEqYe9pkmVrtRP", "t2Ez9KM8VJLuArcxuEkNRAkhNvidKkzXcjJ", "t2D5y7J5fpXajLbGrMBQkFg2mFN8fo3n8cX", "t2UV2wr1PTaUiybpkV3FdSdGxUJeZdZztyt"
]
"vFoundersRewardAddress": [
"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543", "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2", "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy",
"t2BkYdVCHzvTJJUTx4yZB8qeegD8QsPx8bo", "t2J8q1xH1EuigJ52MfExyyjYtN3VgvshKDf", "t2Crq9mydTm37kZokC68HzT6yez3t2FBnFj", "t2EaMPUiQ1kthqcP5UEkF42CAFKJqXCkXC9",
"t2F9dtQc63JDDyrhnfpzvVYTJcr57MkqA12", "t2LPirmnfYSZc481GgZBa6xUGcoovfytBnC", "t26xfxoSw2UV9Pe5o3C8V4YybQD4SESfxtp", "t2D3k4fNdErd66YxtvXEdft9xuLoKD7CcVo",
"t2DWYBkxKNivdmsMiivNJzutaQGqmoRjRnL", "t2C3kFF9iQRxfc4B9zgbWo4dQLLqzqjpuGQ", "t2MnT5tzu9HSKcppRyUNwoTp8MUueuSGNaB", "t2AREsWdoW1F8EQYsScsjkgqobmgrkKeUkK",
"t2Vf4wKcJ3ZFtLj4jezUUKkwYR92BLHn5UT", "t2K3fdViH6R5tRuXLphKyoYXyZhyWGghDNY", "t2VEn3KiKyHSGyzd3nDw6ESWtaCQHwuv9WC", "t2F8XouqdNMq6zzEvxQXHV1TjwZRHwRg8gC",
"t2BS7Mrbaef3fA4xrmkvDisFVXVrRBnZ6Qj", "t2FuSwoLCdBVPwdZuYoHrEzxAb9qy4qjbnL", "t2SX3U8NtrT6gz5Db1AtQCSGjrpptr8JC6h", "t2V51gZNSoJ5kRL74bf9YTtbZuv8Fcqx2FH",
"t2FyTsLjjdm4jeVwir4xzj7FAkUidbr1b4R", "t2EYbGLekmpqHyn8UBF6kqpahrYm7D6N1Le", "t2NQTrStZHtJECNFT3dUBLYA9AErxPCmkka", "t2GSWZZJzoesYxfPTWXkFn5UaxjiYxGBU2a",
"t2RpffkzyLRevGM3w9aWdqMX6bd8uuAK3vn", "t2JzjoQqnuXtTGSN7k7yk5keURBGvYofh1d", "t2AEefc72ieTnsXKmgK2bZNckiwvZe3oPNL", "t2NNs3ZGZFsNj2wvmVd8BSwSfvETgiLrD8J",
"t2ECCQPVcxUCSSQopdNquguEPE14HsVfcUn", "t2JabDUkG8TaqVKYfqDJ3rqkVdHKp6hwXvG", "t2FGzW5Zdc8Cy98ZKmRygsVGi6oKcmYir9n", "t2DUD8a21FtEFn42oVLp5NGbogY13uyjy9t",
"t2UjVSd3zheHPgAkuX8WQW2CiC9xHQ8EvWp", "t2TBUAhELyHUn8i6SXYsXz5Lmy7kDzA1uT5", "t2Tz3uCyhP6eizUWDc3bGH7XUC9GQsEyQNc", "t2NysJSZtLwMLWEJ6MH3BsxRh6h27mNcsSy",
"t2KXJVVyyrjVxxSeazbY9ksGyft4qsXUNm9", "t2J9YYtH31cveiLZzjaE4AcuwVho6qjTNzp", "t2QgvW4sP9zaGpPMH1GRzy7cpydmuRfB4AZ", "t2NDTJP9MosKpyFPHJmfjc5pGCvAU58XGa4",
"t29pHDBWq7qN4EjwSEHg8wEqYe9pkmVrtRP", "t2Ez9KM8VJLuArcxuEkNRAkhNvidKkzXcjJ", "t2D5y7J5fpXajLbGrMBQkFg2mFN8fo3n8cX", "t2UV2wr1PTaUiybpkV3FdSdGxUJeZdZztyt"
]
}

View File

@ -2,5 +2,6 @@
"name": "zclassic",
"symbol": "zcl",
"algorithm": "equihash",
"requireShielding": true,
"peerMagic": "24e92764"
}

View File

@ -19,6 +19,13 @@ module.exports = function(logger, portalConfig, poolConfigs){
return;
case 'pool_stats':
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;
case 'worker_stats':
if (req.url.indexOf("?")>0) {
@ -56,7 +63,6 @@ module.exports = function(logger, portalConfig, poolConfigs){
//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 w in portalStats.stats.pools[pool].workers){
if (w.startsWith(address)) {

View File

@ -1,4 +1,5 @@
var fs = require('fs');
var request = require("request");
var redis = require('redis');
var async = require('async');
@ -50,6 +51,13 @@ function SetupForPool(logger, poolOptions, setupFinished){
var logSystem = 'Payments';
var logComponent = coin;
var opidCount = 0;
var minConfShield = 3;
var minConfPayout = 10;
var requireShielding = poolOptions.coin.requireShielding === true;
logger.special(logSystem, logComponent, logComponent + ' requireShielding: ' + requireShielding);
var daemon = new Stratum.daemon.interface([processingConfig.daemon], function(severity, message){
logger[severity](logSystem, logComponent, message);
@ -279,111 +287,122 @@ function SetupForPool(logger, poolOptions, setupFinished){
);
}
function cacheZCashNetworkStats () {
function cacheNetworkStats () {
var params = null;
daemon.cmd('getmininginfo', params,
function (result) {
var finalRedisCommands = [];
var coin = logComponent;
if (result.error) {
logger.error(logSystem, logComponent, 'Error getting stats from zcashd'
logger.error(logSystem, logComponent, 'Error with RPC call `getmininginfo`'
+ JSON.stringify(result.error));
return;
} else {
logger.special(logSystem, logComponent, "Updating "+logComponent+" network stats...");
var coin = logComponent;
var finalRedisCommands = [];
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', 'networkSols', result[0].response.networksolps]);
redisClient.multi(finalRedisCommands).exec(function(error, results){
if (error){
logger.error(logSystem, logComponent, 'Could not update zcash stats to redis ' + JSON.stringify(error));
return;
}
});
if (result[0].response.blocks !== null) {
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', 'networkSols', result[0].response.networksolps]);
} else {
logger.error(logSystem, logComponent, "Error parse RPC call reponse.blocks tp `getmininginfo`." + JSON.stringify(result[0].response));
}
}
daemon.cmd('getinfo', params,
daemon.cmd('getnetworkinfo', params,
function (result) {
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));
return;
} else {
var coin = logComponent;
var finalRedisCommands = [];
finalRedisCommands.push(['hset', coin + ':stats', 'networkConnections', result[0].response.connections]);
redisClient.multi(finalRedisCommands).exec(function(error, results){
if (error){
logger.error(logSystem, logComponent, 'Could not update zcash stats to redis ' + JSON.stringify(error));
return;
}
});
if (result[0].response !== null) {
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){
if (error){
logger.error(logSystem, logComponent, 'Error update coin stats to redis ' + JSON.stringify(error));
return;
}
});
}
);
);
}
);
}
// run coinbase coin transfers every x minutes
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
setInterval(function() {
intervalState++;
switch (intervalState){
case 1:
listUnspent(poolOptions.address, null, 1, false, sendTToZ);
break;
default:
listUnspentZ(poolOptions.zAddress, 1, false, sendZToT);
//listUnspent(null, poolOptions.address, 1, true, function (){});
intervalState = 0;
break;
// shielding not required for some equihash coins
if (requireShielding === true) {
intervalState++;
switch (intervalState) {
case 1:
listUnspent(poolOptions.address, null, minConfShield, false, sendTToZ);
break;
default:
listUnspentZ(poolOptions.zAddress, minConfShield, false, sendZToT);
intervalState = 0;
break;
}
}
// update zcash stats
cacheZCashNetworkStats();
// update network stats using coin daemon
cacheNetworkStats();
}, interval);
// check operation statuses every x seconds
var opid_interval = poolOptions.walletInterval * 1000;
setInterval(function(){
var checkOpIdSuccessAndGetResult = function(ops) {
ops.forEach(function(op, i){
if (op.status == "success" || op.status == "failed") {
daemon.cmd('z_getoperationresult', [[op.id]], function (result) {
if (result.error) {
logger.warning(logSystem, logComponent, 'Unable to get payment operation id result ' + JSON.stringify(result));
}
if (result.response) {
if (opidCount > 0) {
opidCount = 0;
// shielding not required for some equihash coins
if (requireShielding === true) {
setInterval(function(){
var checkOpIdSuccessAndGetResult = function(ops) {
ops.forEach(function(op, i){
if (op.status == "success" || op.status == "failed") {
daemon.cmd('z_getoperationresult', [[op.id]], function (result) {
if (result.error) {
logger.warning(logSystem, logComponent, 'Unable to get payment operation id result ' + JSON.stringify(result));
}
if (op.status == "failed") {
if (op.error) {
logger.error(logSystem, logComponent, "Payment operation failed " + op.id + " " + op.error.code +", " + op.error.message);
} else {
logger.error(logSystem, logComponent, "Payment operation failed " + op.id);
if (result.response) {
if (opidCount > 0) {
opidCount = 0;
}
if (op.status == "failed") {
if (op.error) {
logger.error(logSystem, logComponent, "Payment operation failed " + op.id + " " + op.error.code +", " + op.error.message);
} else {
logger.error(logSystem, logComponent, "Payment operation failed " + op.id);
}
} else {
logger.special(logSystem, logComponent, 'Payment operation success ' + op.id + ' txid: ' + op.result.txid);
}
} else {
logger.special(logSystem, logComponent, 'Payment operation success ' + op.id + ' txid: ' + op.result.txid);
}
}, true, true);
} else if (op.status == "executing") {
if (opidCount == 0) {
opidCount++;
logger.special(logSystem, logComponent, 'Payment operation in progress ' + op.id );
}
}, true, true);
} else if (op.status == "executing") {
if (opidCount == 0) {
opidCount++;
logger.special(logSystem, logComponent, 'Payment operation in progress ' + op.id );
}
}
});
};
daemon.cmd('z_getoperationstatus', null, function (result) {
if (result.error) {
logger.warning(logSystem, logComponent, 'Unable to get operation ids for clearing.');
}
if (result.response) {
checkOpIdSuccessAndGetResult(result.response);
}
}, true, true);
}, opid_interval);
});
};
daemon.cmd('z_getoperationstatus', null, function (result) {
if (result.error) {
logger.warning(logSystem, logComponent, 'Unable to get operation ids for clearing.');
}
if (result.response) {
checkOpIdSuccessAndGetResult(result.response);
}
}, true, true);
}, opid_interval);
}
var satoshisToCoins = function(satoshis){
return parseFloat((satoshis / magnitude).toFixed(coinPrecision));
@ -478,7 +497,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
}
// 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.confirmations > 100) {
return ['hdel', logComponent + ':blocksPendingConfirms', b.result.hash];
@ -609,10 +628,16 @@ function SetupForPool(logger, poolOptions, setupFinished){
+ round.txHash);
return;
}
// TODO, estimate transaction fees, make dynamic
var fee = 0.00005; // komodo
if (logComponent != "komodo") {
fee = 0.0004; // all other coins
}
round.category = generationTx.category;
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 +668,32 @@ function SetupForPool(logger, poolOptions, setupFinished){
}
});
// check if we have enough tAddress funds to send payments
var totalOwed = 0;
for (var i = 0; i < rounds.length; i++) {
totalOwed = totalOwed + (rounds[i].reward * magnitude) - 4000; // TODO: make tx fees dynamic
// TODO, estimate transaction fees, make dynamic
var fee = 500; // komodo
if (logComponent != "komodo") {
fee = 4000; // all other coins
}
listUnspent(null, poolOptions.address, 1, false, function (error, tBalance){
if (tBalance < totalOwed) {
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)');
// calculate what the pool owes its miners
var totalOwed = parseInt(0);
for (var i = 0; i < rounds.length; i++) {
totalOwed = totalOwed + Math.round(rounds[i].reward * magnitude) - fee; // TODO: make tx fees dynamic
}
var notAddr = null;
if (requireShielding === true) {
notAddr = poolOptions.address;
}
// 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 {
} else {
// zcash daemon does not support account feature
addressAccount = "";
callback(null, workers, rounds, addressAccount);
@ -851,7 +890,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
return parseInt(r.height);
});
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)]);
startRedisTimer();
redisClient.multi(paymentsUpdate).exec(function(error, payments){
@ -882,11 +921,12 @@ function SetupForPool(logger, poolOptions, setupFinished){
},
function(workers, rounds, callback){
var totalPaid = 0;
var totalPaid = parseFloat(0);
var balanceUpdateCommands = [];
var workerPayoutsCommand = [];
// update worker paid/balance stats
for (var w in workers) {
var worker = workers[w];
if (worker.balanceChange !== 0){
@ -916,6 +956,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
}
};
// handle the round
rounds.forEach(function(r){
switch(r.category){
case 'kicked':
@ -994,5 +1035,4 @@ function SetupForPool(logger, poolOptions, setupFinished){
else return address;
};
}

View File

@ -2,7 +2,6 @@ var zlib = require('zlib');
var redis = require('redis');
var async = require('async');
//var fancyTimestamp = require('fancy-timestamp');
var os = require('os');
@ -176,11 +175,18 @@ module.exports = function(logger, portalConfig, poolConfigs){
var client = redisClients[0].client,
coins = redisClients[0].coins,
shares = [];
var totalShares = 0;
var pindex = parseInt(0);
var totalShares = parseFloat(0);
async.each(_this.stats.pools, function(pool, pcb) {
pindex++;
var coin = String(_this.stats.pools[pool.name].name);
client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", function(error, result) {
var workerName = "";
if (error) {
pcb(error);
return;
}
var workerName="";
var shares = 0;
for (var i in result[1]) {
if (Math.abs(i % 2) != 1) {
@ -189,15 +195,20 @@ module.exports = function(logger, portalConfig, poolConfigs){
shares += parseFloat(result[1][i]);
}
}
totalShares = shares;
pcb();
if (shares>0) {
totalShares = shares;
}
pcb();
});
}, function(err) {
if (err) {
cback(0);
return;
}
cback(totalShares);
if (err) {
cback(0);
return;
}
if (totalShares > 0 || (pindex >= Object.keys(_this.stats.pools).length)) {
cback(totalShares);
return;
}
});
};
@ -285,7 +296,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
['smembers', ':blocksConfirmed'],
['hgetall', ':shares:roundCurrent'],
['hgetall', ':blocksPendingConfirms'],
['hgetall', ':payments']
['zrange', ':payments', -100, -1]
];
var commandsPerCoin = redisCommandTemplates.length;
@ -320,23 +331,31 @@ module.exports = function(logger, portalConfig, poolConfigs){
networkSols: 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,
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: {
pending: replies[i + 3],
confirmed: replies[i + 4],
orphaned: replies[i + 5]
},
/* show all pending blocks */
pending: {
blocks: replies[i + 6].sort(sortBlocks),
confirms: replies[i + 9]
},
/* show last 5 found blocks */
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]
};
for(var j = replies[i + 10].length; j > 0; j--){
coinStats.payments.push(JSON.parse(replies[i + 10][j-1]));
}
/*
for (var b in coinStats.confirmed.blocks) {
var parms = coinStats.confirmed.blocks[b].split(':');
@ -460,7 +479,8 @@ module.exports = function(logger, portalConfig, poolConfigs){
var shareMultiplier = Math.pow(2, 32) / algos[coinStats.algorithm].multiplier;
coinStats.hashrate = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow;
coinStats.hashrateString = _this.getReadableHashRateString(coinStats.hashrate);
var _blocktime = 250;
var _blocktime = 160;
var _networkHashRate = parseFloat(coinStats.poolStats.networkSols) * 1.2;
var _myHashRate = (coinStats.hashrate / 1000000) * 2;
coinStats.luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
@ -481,7 +501,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
portalStats.algos[algo].hashrate += coinStats.hashrate;
portalStats.algos[algo].workers += Object.keys(coinStats.workers).length;
var _shareTotal = 0;
var _shareTotal = parseFloat(0);
for (var worker in coinStats.currentRoundShares) {
var miner = worker.split(".")[0];
if (miner in coinStats.miners) {
@ -492,10 +512,9 @@ module.exports = function(logger, portalConfig, poolConfigs){
}
_shareTotal += parseFloat(coinStats.currentRoundShares[worker]);
}
coinStats.shareCount = Math.round(_shareTotal * 100) / 100;
coinStats.shareCount = _shareTotal;
for (var worker in coinStats.workers) {
var _blocktime = 250;
var _workerRate = shareMultiplier * coinStats.workers[worker].shares / portalConfig.website.stats.hashrateWindow;
var _wHashRate = (_workerRate / 1000000) * 2;
coinStats.workers[worker].luckDays = ((_networkHashRate / _wHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
@ -504,7 +523,6 @@ module.exports = function(logger, portalConfig, poolConfigs){
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate);
}
for (var miner in coinStats.miners) {
var _blocktime = 250;
var _workerRate = shareMultiplier * coinStats.miners[miner].shares / portalConfig.website.stats.hashrateWindow;
var _wHashRate = (_workerRate / 1000000) * 2;
coinStats.miners[miner].luckDays = ((_networkHashRate / _wHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
@ -524,13 +542,20 @@ module.exports = function(logger, portalConfig, poolConfigs){
var algoStats = portalStats.algos[algo];
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.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);

View File

@ -230,7 +230,6 @@ module.exports = function(logger){
var payout = function(req, res, next){
var address = req.params.address || null;
if (address != null){
portalStats.getPayout(address, function(data){
res.write(data.toString());
@ -241,26 +240,20 @@ module.exports = function(logger){
next();
};
var shares = function(req, res, next){
portalStats.getCoins(function(){
processTemplates();
res.end(indexesProcessed['user_shares']);
});
};
var usershares = function(req, res, next){
var coin = req.params.coin || null;
if(coin != null){
portalStats.getCoinTotals(coin, null, function(){
processTemplates();
res.end(indexesProcessed['user_shares']);
});
}
else

View File

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

View File

@ -88,8 +88,8 @@
<div class="chartHolder"><svg id="workerHashrate" /></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: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="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</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> </div>
</div>
</div>
</div>