Feat/pool fee in api (#39)

* Initial poolFee commit

This is defined by the following in the pool_config

```
    "poolFee": 0.2,
    "_comment_poolFee": "This is only used for API purpose to display pool fee percentage",
```

* Added poolFee function

This function looks at the user defined `poolFee` in their pool_config and if it doesn't exist it will calculate it based on `rewardRecipients`

* Fix to show %

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Added poolFee

* Spacing fix

* Changing gears -- instead of providing additional configuration, we're
just going to expose the rewardRecipients. This allows the consuming
client to filter community donation addresses, etc. as needed.

In addition, I've replaced all tabs in libs/stats.js with spaces.
This commit is contained in:
zzzpotato 2018-10-31 16:47:01 -05:00 committed by Nate Smith
parent d7826dcad7
commit 250377de5a
1 changed files with 269 additions and 266 deletions

View File

@ -26,31 +26,31 @@ function rediscreateClient(port, host, pass) {
* @returns {Array} array of items in [[key,value],[key,value],...] format. * @returns {Array} array of items in [[key,value],[key,value],...] format.
*/ */
function sortProperties(obj, sortedBy, isNumericSort, reverse) { function sortProperties(obj, sortedBy, isNumericSort, reverse) {
sortedBy = sortedBy || 1; // by default first key sortedBy = sortedBy || 1; // by default first key
isNumericSort = isNumericSort || false; // by default text sort isNumericSort = isNumericSort || false; // by default text sort
reverse = reverse || false; // by default no reverse reverse = reverse || false; // by default no reverse
var reversed = (reverse) ? -1 : 1; var reversed = (reverse) ? -1 : 1;
var sortable = []; var sortable = [];
for (var key in obj) { for (var key in obj) {
if (obj.hasOwnProperty(key)) { if (obj.hasOwnProperty(key)) {
sortable.push([key, obj[key]]); sortable.push([key, obj[key]]);
} }
} }
if (isNumericSort) if (isNumericSort)
sortable.sort(function (a, b) { sortable.sort(function (a, b) {
return reversed * (a[1][sortedBy] - b[1][sortedBy]); return reversed * (a[1][sortedBy] - b[1][sortedBy]);
}); });
else else
sortable.sort(function (a, b) { sortable.sort(function (a, b) {
var x = a[1][sortedBy].toLowerCase(), var x = a[1][sortedBy].toLowerCase(),
y = b[1][sortedBy].toLowerCase(); y = b[1][sortedBy].toLowerCase();
return x < y ? reversed * -1 : x > y ? reversed : 0; return x < y ? reversed * -1 : x > y ? reversed : 0;
}); });
return sortable; // array in format [ [ key1, val1 ], [ key2, val2 ], ... ] return sortable; // array in format [ [ key1, val1 ], [ key2, val2 ], ... ]
} }
module.exports = function(logger, portalConfig, poolConfigs){ module.exports = function(logger, portalConfig, poolConfigs){
var _this = this; var _this = this;
@ -104,17 +104,17 @@ module.exports = function(logger, portalConfig, poolConfigs){
if (_this.stats.pools[pool.name].pending && _this.stats.pools[pool.name].pending.blocks) if (_this.stats.pools[pool.name].pending && _this.stats.pools[pool.name].pending.blocks)
for (var i=0; i<_this.stats.pools[pool.name].pending.blocks.length; i++) for (var i=0; i<_this.stats.pools[pool.name].pending.blocks.length; i++)
allBlocks[pool.name+"-"+_this.stats.pools[pool.name].pending.blocks[i].split(':')[2]] = _this.stats.pools[pool.name].pending.blocks[i]; allBlocks[pool.name+"-"+_this.stats.pools[pool.name].pending.blocks[i].split(':')[2]] = _this.stats.pools[pool.name].pending.blocks[i];
if (_this.stats.pools[pool.name].confirmed && _this.stats.pools[pool.name].confirmed.blocks) if (_this.stats.pools[pool.name].confirmed && _this.stats.pools[pool.name].confirmed.blocks)
for (var i=0; i<_this.stats.pools[pool.name].confirmed.blocks.length; i++) for (var i=0; i<_this.stats.pools[pool.name].confirmed.blocks.length; i++)
allBlocks[pool.name+"-"+_this.stats.pools[pool.name].confirmed.blocks[i].split(':')[2]] = _this.stats.pools[pool.name].confirmed.blocks[i]; allBlocks[pool.name+"-"+_this.stats.pools[pool.name].confirmed.blocks[i].split(':')[2]] = _this.stats.pools[pool.name].confirmed.blocks[i];
pcb(); pcb();
}, function(err) { }, function(err) {
cback(allBlocks); cback(allBlocks);
}); });
}; };
function gatherStatHistory(){ function gatherStatHistory(){
var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0).toString(); var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0).toString();
redisStats.zrangebyscore(['statHistory', retentionTime, '+inf'], function(err, replies){ redisStats.zrangebyscore(['statHistory', retentionTime, '+inf'], function(err, replies){
@ -134,34 +134,34 @@ module.exports = function(logger, portalConfig, poolConfigs){
}); });
} }
function getWorkerStats(address) { function getWorkerStats(address) {
address = address.split(".")[0]; address = address.split(".")[0];
if (address.length > 0 && address.startsWith('t')) { if (address.length > 0 && address.startsWith('t')) {
for (var h in statHistory) { for (var h in statHistory) {
for(var pool in statHistory[h].pools) { for(var pool in statHistory[h].pools) {
statHistory[h].pools[pool].workers.sort(sortWorkersByHashrate); statHistory[h].pools[pool].workers.sort(sortWorkersByHashrate);
for(var w in statHistory[h].pools[pool].workers){
if (w.startsWith(address)) {
if (history[w] == null) {
history[w] = [];
}
if (workers[w] == null && stats.pools[pool].workers[w] != null) {
workers[w] = stats.pools[pool].workers[w];
}
if (statHistory[h].pools[pool].workers[w].hashrate) {
history[w].push({time: statHistory[h].time, hashrate:statHistory[h].pools[pool].workers[w].hashrate});
}
}
}
}
}
return JSON.stringify({"workers": workers, "history": history});
}
return null;
}
for(var w in statHistory[h].pools[pool].workers){
if (w.startsWith(address)) {
if (history[w] == null) {
history[w] = [];
}
if (workers[w] == null && stats.pools[pool].workers[w] != null) {
workers[w] = stats.pools[pool].workers[w];
}
if (statHistory[h].pools[pool].workers[w].hashrate) {
history[w].push({time: statHistory[h].time, hashrate:statHistory[h].pools[pool].workers[w].hashrate});
}
}
}
}
}
return JSON.stringify({"workers": workers, "history": history});
}
return null;
}
function addStatPoolHistory(stats){ function addStatPoolHistory(stats){
var data = { var data = {
time: stats.time, time: stats.time,
@ -176,10 +176,10 @@ module.exports = function(logger, portalConfig, poolConfigs){
} }
_this.statPoolHistory.push(data); _this.statPoolHistory.push(data);
} }
var magnitude = 100000000; var magnitude = 100000000;
var coinPrecision = magnitude.toString().length - 1; var coinPrecision = magnitude.toString().length - 1;
function roundTo(n, digits) { function roundTo(n, digits) {
if (digits === undefined) { if (digits === undefined) {
digits = 0; digits = 0;
@ -193,7 +193,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
var satoshisToCoins = function(satoshis){ var satoshisToCoins = function(satoshis){
return roundTo((satoshis / magnitude), coinPrecision); return roundTo((satoshis / magnitude), coinPrecision);
}; };
var coinsToSatoshies = function(coins){ var coinsToSatoshies = function(coins){
return Math.round(coins * magnitude); return Math.round(coins * magnitude);
}; };
@ -201,7 +201,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
function coinsRound(number) { function coinsRound(number) {
return roundTo(number, coinPrecision); return roundTo(number, coinPrecision);
} }
function readableSeconds(t) { function readableSeconds(t) {
var seconds = Math.round(t); var seconds = Math.round(t);
var minutes = Math.floor(seconds/60); var minutes = Math.floor(seconds/60);
@ -220,7 +220,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
_this.stats.coins = redisClients[0].coins; _this.stats.coins = redisClients[0].coins;
cback(); cback();
}; };
this.getPayout = function(address, cback){ this.getPayout = function(address, cback){
async.waterfall([ async.waterfall([
function(callback){ function(callback){
@ -232,38 +232,38 @@ module.exports = function(logger, portalConfig, poolConfigs){
cback(coinsRound(total).toFixed(8)); cback(coinsRound(total).toFixed(8));
}); });
}; };
this.getTotalSharesByAddress = function(address, cback) { this.getTotalSharesByAddress = function(address, cback) {
var a = address.split(".")[0]; var a = address.split(".")[0];
var client = redisClients[0].client, var client = redisClients[0].client,
coins = redisClients[0].coins, coins = redisClients[0].coins,
shares = []; shares = [];
var pindex = parseInt(0); var pindex = parseInt(0);
var totalShares = parseFloat(0); var totalShares = parseFloat(0);
async.each(_this.stats.pools, function(pool, pcb) { async.each(_this.stats.pools, function(pool, pcb) {
pindex++; 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+"*", "count", 1000, function(error, result) { client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", "count", 1000, function(error, result) {
if (error) { if (error) {
pcb(error); pcb(error);
return; return;
} }
var workerName=""; 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) {
workerName = String(result[1][i]); workerName = String(result[1][i]);
} else { } else {
shares += parseFloat(result[1][i]); shares += parseFloat(result[1][i]);
} }
} }
if (shares>0) { if (shares>0) {
totalShares = shares; totalShares = shares;
} }
pcb(); pcb();
}); });
}, function(err) { }, function(err) {
if (err) { if (err) {
cback(0); cback(0);
return; return;
@ -272,37 +272,37 @@ module.exports = function(logger, portalConfig, poolConfigs){
cback(totalShares); cback(totalShares);
return; return;
} }
}); });
}; };
this.getBalanceByAddress = function(address, cback){ this.getBalanceByAddress = function(address, cback){
var a = address.split(".")[0]; var a = address.split(".")[0];
var client = redisClients[0].client, var client = redisClients[0].client,
coins = redisClients[0].coins, coins = redisClients[0].coins,
balances = []; balances = [];
var totalHeld = parseFloat(0); var totalHeld = parseFloat(0);
var totalPaid = parseFloat(0); var totalPaid = parseFloat(0);
var totalImmature = parseFloat(0); var totalImmature = parseFloat(0);
async.each(_this.stats.pools, function(pool, pcb) { async.each(_this.stats.pools, function(pool, pcb) {
var coin = String(_this.stats.pools[pool.name].name); var coin = String(_this.stats.pools[pool.name].name);
// get all immature balances from address // get all immature balances from address
client.hscan(coin + ':immature', 0, "match", a+"*", "count", 10000, function(error, pends) { client.hscan(coin + ':immature', 0, "match", a+"*", "count", 10000, function(error, pends) {
// get all balances from address // get all balances from address
client.hscan(coin + ':balances', 0, "match", a+"*", "count", 10000, function(error, bals) { client.hscan(coin + ':balances', 0, "match", a+"*", "count", 10000, function(error, bals) {
// get all payouts from address // get all payouts from address
client.hscan(coin + ':payouts', 0, "match", a+"*", "count", 10000, function(error, pays) { client.hscan(coin + ':payouts', 0, "match", a+"*", "count", 10000, function(error, pays) {
var workerName = ""; var workerName = "";
var balAmount = 0; var balAmount = 0;
var paidAmount = 0; var paidAmount = 0;
var pendingAmount = 0; var pendingAmount = 0;
var workers = {}; var workers = {};
for (var i in pays[1]) { for (var i in pays[1]) {
if (Math.abs(i % 2) != 1) { if (Math.abs(i % 2) != 1) {
workerName = String(pays[1][i]); workerName = String(pays[1][i]);
@ -333,7 +333,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
totalImmature += pendingAmount; totalImmature += pendingAmount;
} }
} }
for (var w in workers) { for (var w in workers) {
balances.push({ balances.push({
worker:String(w), worker:String(w),
@ -342,23 +342,23 @@ module.exports = function(logger, portalConfig, poolConfigs){
immature:workers[w].immature immature:workers[w].immature
}); });
} }
pcb(); pcb();
}); });
}); });
}); });
}, function(err) { }, function(err) {
if (err) { if (err) {
callback("There was an error getting balances"); callback("There was an error getting balances");
return; return;
} }
_this.stats.balances = balances;
_this.stats.address = address;
cback({totalHeld:coinsRound(totalHeld), totalPaid:coinsRound(totalPaid), totalImmature:satoshisToCoins(totalImmature), balances}); _this.stats.balances = balances;
}); _this.stats.address = address;
};
cback({totalHeld:coinsRound(totalHeld), totalPaid:coinsRound(totalPaid), totalImmature:satoshisToCoins(totalImmature), balances});
});
};
this.getGlobalStats = function(callback){ this.getGlobalStats = function(callback){
@ -377,9 +377,9 @@ module.exports = function(logger, portalConfig, poolConfigs){
['scard', ':blocksPending'], ['scard', ':blocksPending'],
['scard', ':blocksConfirmed'], ['scard', ':blocksConfirmed'],
['scard', ':blocksKicked'], ['scard', ':blocksKicked'],
['smembers', ':blocksPending'], ['smembers', ':blocksPending'],
['smembers', ':blocksConfirmed'], ['smembers', ':blocksConfirmed'],
['hgetall', ':shares:roundCurrent'], ['hgetall', ':shares:roundCurrent'],
['hgetall', ':blocksPendingConfirms'], ['hgetall', ':blocksPendingConfirms'],
['zrange', ':payments', -100, -1], ['zrange', ':payments', -100, -1],
['hgetall', ':shares:timesCurrent'] ['hgetall', ':shares:timesCurrent']
@ -401,6 +401,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
callback(err); callback(err);
} }
else{ else{
for(var i = 0; i < replies.length; i += commandsPerCoin){ for(var i = 0; i < replies.length; i += commandsPerCoin){
var coinName = client.coins[i / commandsPerCoin | 0]; var coinName = client.coins[i / commandsPerCoin | 0];
var marketStats = {}; var marketStats = {};
@ -409,21 +410,23 @@ module.exports = function(logger, portalConfig, poolConfigs){
marketStats = replies[i + 2] ? (JSON.parse(replies[i + 2].coinmarketcap)[0] || 0) : 0; marketStats = replies[i + 2] ? (JSON.parse(replies[i + 2].coinmarketcap)[0] || 0) : 0;
} }
} }
var coinStats = { var coinStats = {
name: coinName, name: coinName,
symbol: poolConfigs[coinName].coin.symbol.toUpperCase(), symbol: poolConfigs[coinName].coin.symbol.toUpperCase(),
algorithm: poolConfigs[coinName].coin.algorithm, algorithm: poolConfigs[coinName].coin.algorithm,
poolFees: poolConfigs[coinName].rewardRecipients,
hashrates: replies[i + 1], hashrates: replies[i + 1],
poolStats: { poolStats: {
validShares: replies[i + 2] ? (replies[i + 2].validShares || 0) : 0, validShares: replies[i + 2] ? (replies[i + 2].validShares || 0) : 0,
validBlocks: replies[i + 2] ? (replies[i + 2].validBlocks || 0) : 0, validBlocks: replies[i + 2] ? (replies[i + 2].validBlocks || 0) : 0,
invalidShares: replies[i + 2] ? (replies[i + 2].invalidShares || 0) : 0, invalidShares: replies[i + 2] ? (replies[i + 2].invalidShares || 0) : 0,
totalPaid: replies[i + 2] ? (replies[i + 2].totalPaid || 0) : 0, totalPaid: replies[i + 2] ? (replies[i + 2].totalPaid || 0) : 0,
networkBlocks: replies[i + 2] ? (replies[i + 2].networkBlocks || 0) : 0, networkBlocks: replies[i + 2] ? (replies[i + 2].networkBlocks || 0) : 0,
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, networkVersion: replies[i + 2] ? (replies[i + 2].networkSubVersion || 0) : 0,
networkProtocolVersion: replies[i + 2] ? (replies[i + 2].networkProtocolVersion || 0) : 0 networkProtocolVersion: replies[i + 2] ? (replies[i + 2].networkProtocolVersion || 0) : 0
}, },
@ -435,16 +438,16 @@ module.exports = function(logger, portalConfig, poolConfigs){
orphaned: replies[i + 5] orphaned: replies[i + 5]
}, },
/* show all pending blocks */ /* 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 50 found blocks */ /* show last 50 found blocks */
confirmed: { confirmed: {
blocks: replies[i + 7].sort(sortBlocks).slice(0,50) blocks: replies[i + 7].sort(sortBlocks).slice(0,50)
}, },
payments: [], payments: [],
currentRoundShares: (replies[i + 8] || {}), currentRoundShares: (replies[i + 8] || {}),
currentRoundTimes: (replies[i + 11] || {}), currentRoundTimes: (replies[i + 11] || {}),
maxRoundTime: 0, maxRoundTime: 0,
shareCount: 0 shareCount: 0
@ -456,7 +459,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
} catch(e) { } catch(e) {
jsonObj = null; jsonObj = null;
} }
if (jsonObj !== null) { if (jsonObj !== null) {
coinStats.payments.push(jsonObj); coinStats.payments.push(jsonObj);
} }
} }
@ -487,106 +490,106 @@ module.exports = function(logger, portalConfig, poolConfigs){
Object.keys(allCoinStats).forEach(function(coin){ Object.keys(allCoinStats).forEach(function(coin){
var coinStats = allCoinStats[coin]; var coinStats = allCoinStats[coin];
coinStats.workers = {}; coinStats.workers = {};
coinStats.miners = {}; coinStats.miners = {};
coinStats.shares = 0; coinStats.shares = 0;
coinStats.hashrates.forEach(function(ins){ coinStats.hashrates.forEach(function(ins){
var parts = ins.split(':'); var parts = ins.split(':');
var workerShares = parseFloat(parts[0]); var workerShares = parseFloat(parts[0]);
var miner = parts[1].split('.')[0]; var miner = parts[1].split('.')[0];
var worker = parts[1]; var worker = parts[1];
var diff = Math.round(parts[0] * 8192); var diff = Math.round(parts[0] * 8192);
if (workerShares > 0) { if (workerShares > 0) {
coinStats.shares += workerShares; coinStats.shares += workerShares;
// build worker stats // build worker stats
if (worker in coinStats.workers) { if (worker in coinStats.workers) {
coinStats.workers[worker].shares += workerShares; coinStats.workers[worker].shares += workerShares;
coinStats.workers[worker].diff = diff; coinStats.workers[worker].diff = diff;
} else { } else {
coinStats.workers[worker] = { coinStats.workers[worker] = {
name: worker, name: worker,
diff: diff, diff: diff,
shares: workerShares, shares: workerShares,
invalidshares: 0, invalidshares: 0,
currRoundShares: 0, currRoundShares: 0,
currRoundTime: 0, currRoundTime: 0,
hashrate: null, hashrate: null,
hashrateString: null, hashrateString: null,
luckDays: null, luckDays: null,
luckHours: null, luckHours: null,
paid: 0, paid: 0,
balance: 0 balance: 0
}; };
} }
// build miner stats // build miner stats
if (miner in coinStats.miners) { if (miner in coinStats.miners) {
coinStats.miners[miner].shares += workerShares; coinStats.miners[miner].shares += workerShares;
} else { } else {
coinStats.miners[miner] = { coinStats.miners[miner] = {
name: miner, name: miner,
shares: workerShares, shares: workerShares,
invalidshares: 0, invalidshares: 0,
currRoundShares: 0, currRoundShares: 0,
currRoundTime: 0, currRoundTime: 0,
hashrate: null, hashrate: null,
hashrateString: null, hashrateString: null,
luckDays: null, luckDays: null,
luckHours: null luckHours: null
}; };
} }
} }
else { else {
// build worker stats // build worker stats
if (worker in coinStats.workers) { if (worker in coinStats.workers) {
coinStats.workers[worker].invalidshares -= workerShares; // workerShares is negative number! coinStats.workers[worker].invalidshares -= workerShares; // workerShares is negative number!
coinStats.workers[worker].diff = diff; coinStats.workers[worker].diff = diff;
} else { } else {
coinStats.workers[worker] = { coinStats.workers[worker] = {
name: worker, name: worker,
diff: diff, diff: diff,
shares: 0, shares: 0,
invalidshares: -workerShares, invalidshares: -workerShares,
currRoundShares: 0, currRoundShares: 0,
currRoundTime: 0, currRoundTime: 0,
hashrate: null, hashrate: null,
hashrateString: null, hashrateString: null,
luckDays: null, luckDays: null,
luckHours: null, luckHours: null,
paid: 0, paid: 0,
balance: 0 balance: 0
}; };
} }
// build miner stats // build miner stats
if (miner in coinStats.miners) { if (miner in coinStats.miners) {
coinStats.miners[miner].invalidshares -= workerShares; // workerShares is negative number! coinStats.miners[miner].invalidshares -= workerShares; // workerShares is negative number!
} else { } else {
coinStats.miners[miner] = { coinStats.miners[miner] = {
name: miner, name: miner,
shares: 0, shares: 0,
invalidshares: -workerShares, invalidshares: -workerShares,
currRoundShares: 0, currRoundShares: 0,
currRoundTime: 0, currRoundTime: 0,
hashrate: null, hashrate: null,
hashrateString: null, hashrateString: null,
luckDays: null, luckDays: null,
luckHours: null luckHours: null
}; };
} }
} }
}); });
// sort miners // sort miners
coinStats.miners = sortMinersByHashrate(coinStats.miners); coinStats.miners = sortMinersByHashrate(coinStats.miners);
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 = 160; 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);
coinStats.luckHours = ((_networkHashRate / _myHashRate * _blocktime) / (60 * 60)).toFixed(3); coinStats.luckHours = ((_networkHashRate / _myHashRate * _blocktime) / (60 * 60)).toFixed(3);
coinStats.minerCount = Object.keys(coinStats.miners).length; coinStats.minerCount = Object.keys(coinStats.miners).length;
coinStats.workerCount = Object.keys(coinStats.workers).length; coinStats.workerCount = Object.keys(coinStats.workers).length;
portalStats.global.workers += coinStats.workerCount; portalStats.global.workers += coinStats.workerCount;
@ -617,7 +620,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
for (var worker in coinStats.currentRoundTimes) { for (var worker in coinStats.currentRoundTimes) {
var time = parseFloat(coinStats.currentRoundTimes[worker]); var time = parseFloat(coinStats.currentRoundTimes[worker]);
if (_maxTimeShare < time) { _maxTimeShare = time; } if (_maxTimeShare < time) { _maxTimeShare = time; }
var miner = worker.split(".")[0]; // split poolId from minerAddress var miner = worker.split(".")[0]; // split poolId from minerAddress
if (miner in coinStats.miners && coinStats.miners[miner].currRoundTime < time) { if (miner in coinStats.miners && coinStats.miners[miner].currRoundTime < time) {
coinStats.miners[miner].currRoundTime = time; coinStats.miners[miner].currRoundTime = time;
} }
@ -626,31 +629,31 @@ module.exports = function(logger, portalConfig, poolConfigs){
coinStats.shareCount = _shareTotal; coinStats.shareCount = _shareTotal;
coinStats.maxRoundTime = _maxTimeShare; coinStats.maxRoundTime = _maxTimeShare;
coinStats.maxRoundTimeString = readableSeconds(_maxTimeShare); coinStats.maxRoundTimeString = readableSeconds(_maxTimeShare);
for (var worker in coinStats.workers) { for (var worker in coinStats.workers) {
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);
coinStats.workers[worker].luckHours = ((_networkHashRate / _wHashRate * _blocktime) / (60 * 60)).toFixed(3); coinStats.workers[worker].luckHours = ((_networkHashRate / _wHashRate * _blocktime) / (60 * 60)).toFixed(3);
coinStats.workers[worker].hashrate = _workerRate; coinStats.workers[worker].hashrate = _workerRate;
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate); coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate);
var miner = worker.split('.')[0]; var miner = worker.split('.')[0];
if (miner in coinStats.miners) { if (miner in coinStats.miners) {
coinStats.workers[worker].currRoundTime = coinStats.miners[miner].currRoundTime; coinStats.workers[worker].currRoundTime = coinStats.miners[miner].currRoundTime;
} }
} }
for (var miner in coinStats.miners) { for (var miner in coinStats.miners) {
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);
coinStats.miners[miner].luckHours = ((_networkHashRate / _wHashRate * _blocktime) / (60 * 60)).toFixed(3); coinStats.miners[miner].luckHours = ((_networkHashRate / _wHashRate * _blocktime) / (60 * 60)).toFixed(3);
coinStats.miners[miner].hashrate = _workerRate; coinStats.miners[miner].hashrate = _workerRate;
coinStats.miners[miner].hashrateString = _this.getReadableHashRateString(_workerRate); coinStats.miners[miner].hashrateString = _this.getReadableHashRateString(_workerRate);
} }
// sort workers by name // sort workers by name
coinStats.workers = sortWorkersByName(coinStats.workers); coinStats.workers = sortWorkersByName(coinStats.workers);
delete coinStats.hashrates; delete coinStats.hashrates;
delete coinStats.shares; delete coinStats.shares;
}); });
@ -661,7 +664,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
}); });
_this.stats = portalStats; _this.stats = portalStats;
// save historical hashrate, not entire stats! // save historical hashrate, not entire stats!
var saveStats = JSON.parse(JSON.stringify(portalStats)); var saveStats = JSON.parse(JSON.stringify(portalStats));
Object.keys(saveStats.pools).forEach(function(pool){ Object.keys(saveStats.pools).forEach(function(pool){
@ -674,9 +677,9 @@ module.exports = function(logger, portalConfig, poolConfigs){
}); });
_this.statsString = JSON.stringify(saveStats); _this.statsString = JSON.stringify(saveStats);
_this.statHistory.push(saveStats); _this.statHistory.push(saveStats);
addStatPoolHistory(portalStats); addStatPoolHistory(portalStats);
var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0); var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0);
for (var i = 0; i < _this.statHistory.length; i++){ for (var i = 0; i < _this.statHistory.length; i++){
@ -702,16 +705,16 @@ module.exports = function(logger, portalConfig, poolConfigs){
}; };
function sortPoolsByName(objects) { function sortPoolsByName(objects) {
var newObject = {}; var newObject = {};
var sortedArray = sortProperties(objects, 'name', false, false); var sortedArray = sortProperties(objects, 'name', false, false);
for (var i = 0; i < sortedArray.length; i++) { for (var i = 0; i < sortedArray.length; i++) {
var key = sortedArray[i][0]; var key = sortedArray[i][0];
var value = sortedArray[i][1]; var value = sortedArray[i][1];
newObject[key] = value; newObject[key] = value;
} }
return newObject; return newObject;
} }
function sortBlocks(a, b) { function sortBlocks(a, b) {
var as = parseInt(a.split(":")[2]); var as = parseInt(a.split(":")[2]);
var bs = parseInt(b.split(":")[2]); var bs = parseInt(b.split(":")[2]);
@ -719,56 +722,56 @@ module.exports = function(logger, portalConfig, poolConfigs){
if (as < bs) return 1; if (as < bs) return 1;
return 0; return 0;
} }
function sortWorkersByName(objects) { function sortWorkersByName(objects) {
var newObject = {}; var newObject = {};
var sortedArray = sortProperties(objects, 'name', false, false); var sortedArray = sortProperties(objects, 'name', false, false);
for (var i = 0; i < sortedArray.length; i++) { for (var i = 0; i < sortedArray.length; i++) {
var key = sortedArray[i][0]; var key = sortedArray[i][0];
var value = sortedArray[i][1]; var value = sortedArray[i][1];
newObject[key] = value; newObject[key] = value;
} }
return newObject; return newObject;
} }
function sortMinersByHashrate(objects) { function sortMinersByHashrate(objects) {
var newObject = {}; var newObject = {};
var sortedArray = sortProperties(objects, 'shares', true, true); var sortedArray = sortProperties(objects, 'shares', true, true);
for (var i = 0; i < sortedArray.length; i++) { for (var i = 0; i < sortedArray.length; i++) {
var key = sortedArray[i][0]; var key = sortedArray[i][0];
var value = sortedArray[i][1]; var value = sortedArray[i][1];
newObject[key] = value; newObject[key] = value;
} }
return newObject; return newObject;
} }
function sortWorkersByHashrate(a, b) { function sortWorkersByHashrate(a, b) {
if (a.hashrate === b.hashrate) { if (a.hashrate === b.hashrate) {
return 0; return 0;
} }
else { else {
return (a.hashrate < b.hashrate) ? -1 : 1; return (a.hashrate < b.hashrate) ? -1 : 1;
} }
} }
this.getReadableHashRateString = function(hashrate){ this.getReadableHashRateString = function(hashrate){
hashrate = (hashrate * 2); hashrate = (hashrate * 2);
if (hashrate < 1000000) { if (hashrate < 1000000) {
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s';
} }
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ];
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1);
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); hashrate = (hashrate/1000) / Math.pow(1000, i + 1);
return hashrate.toFixed(2) + byteUnits[i]; return hashrate.toFixed(2) + byteUnits[i];
}; };
function getReadableNetworkHashRateString(hashrate) { function getReadableNetworkHashRateString(hashrate) {
hashrate = (hashrate * 1000000); hashrate = (hashrate * 1000000);
if (hashrate < 1000000) if (hashrate < 1000000)
return '0 Sol'; return '0 Sol';
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ];
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1);
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); hashrate = (hashrate/1000) / Math.pow(1000, i + 1);
return hashrate.toFixed(2) + byteUnits[i]; return hashrate.toFixed(2) + byteUnits[i];
} }
}; };