diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
new file mode 100644
index 000000000..fe461b424
--- /dev/null
+++ b/.github/workflows/dependency-review.yml
@@ -0,0 +1,20 @@
+# Dependency Review Action
+#
+# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
+#
+# Source repository: https://github.com/actions/dependency-review-action
+# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
+name: 'Dependency Review'
+on: [pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout Repository'
+ uses: actions/checkout@v3
+ - name: 'Dependency Review'
+ uses: actions/dependency-review-action@v2
diff --git a/config_example.json b/config_example.json
index 52d28f631..d25bd57e0 100644
--- a/config_example.json
+++ b/config_example.json
@@ -24,6 +24,15 @@
"checkThreshold": 500,
"purgeInterval": 300
},
+ "bannedAddresses": {
+ "enabled": false,
+ "banned": [
+ "banned address 1",
+ "banned address 2",
+ "etcetera"
+ ]
+ },
+
"redis": {
"_disabled_socket": "/var/run/redis/redis.sock",
"_socket": "Set socket to enable UNIX domain sockets, otherwise leave unset and set host and port.",
diff --git a/init.js b/init.js
index 07dfabbd6..a034d3ae7 100644
--- a/init.js
+++ b/init.js
@@ -290,8 +290,9 @@ var spawnPoolWorkers = function(){
var redisCommands = [];
- // if its been less than 15 minutes since last share was submitted
- var timeChangeSec = roundTo(Math.max(now - lastShareTime, 0) / 1000, 4);
+ // if its been less than 15 minutes since last share was submitted by any stratum
+ var lastShareTimeUnified = Math.max(redisCommands.push(['hget', msg.coin + ':lastSeen', workerAddress]), lastShareTime);
+ var timeChangeSec = roundTo(Math.max(now - lastShareTimeUnified, 0) / 1000, 4);
//var timeChangeTotal = roundTo(Math.max(now - lastStartTime, 0) / 1000, 4);
if (timeChangeSec < 900) {
// loyal miner keeps mining :)
diff --git a/libs/api.js b/libs/api.js
index 1b4161174..6487ad817 100644
--- a/libs/api.js
+++ b/libs/api.js
@@ -29,6 +29,59 @@ module.exports = function(logger, portalConfig, poolConfigs){
res.end(JSON.stringify(data));
});
break;
+ case 'worker_balances':
+ res.header('Content-Type', 'application/json');
+ if (req.url.indexOf("?") > 0) {
+ var url_parms = req.url.split("?");
+ if (url_parms.length > 0) {
+ var address = url_parms[1] || null;
+ if (address != null && address.length > 0) {
+ address = address.split(".")[0];
+ //portalStats.getPoolBalancesByAddress(address, function(balances) {
+ // res.end(JSON.stringify(balances));
+ //});
+ portalStats.getPoolBalancesByAddress(address, function(balances) {
+ var formattedBalances = {};
+
+ balances.forEach(function (balance) {
+ if (!formattedBalances[balance.pool]) {
+ formattedBalances[balance.pool] = {
+ name: balance.pool,
+ totalPaid: 0,
+ totalBalance: 0,
+ totalImmature: 0,
+ workers: []
+ };
+ }
+
+ formattedBalances[balance.pool].totalPaid += balance.paid;
+ formattedBalances[balance.pool].totalBalance += balance.balance;
+ formattedBalances[balance.pool].totalImmature += balance.immature;
+
+ formattedBalances[balance.pool].workers.push({
+ name: balance.worker,
+ balance: balance.balance,
+ paid: balance.paid,
+ immature: balance.immature
+ });
+ formattedBalances[balance.pool].totalPaid = (Math.round(formattedBalances[balance.pool].totalPaid * 100000000) / 100000000);
+ formattedBalances[balance.pool].totalBalance = (Math.round(formattedBalances[balance.pool].totalBalance * 100000000) / 100000000);
+ formattedBalances[balance.pool].totalImmature = (Math.round(formattedBalances[balance.pool].totalImmature * 100000000) / 100000000);
+ });
+
+ var finalBalances = Object.values(formattedBalances);
+ res.end(JSON.stringify(finalBalances));
+ });
+ } else {
+ res.end(JSON.stringify({ result: "error", message: "Invalid wallet address" }));
+ }
+ } else {
+ res.end(JSON.stringify({ result: "error", message: "Invalid URL parameters" }));
+ }
+ } else {
+ res.end(JSON.stringify({ result: "error", message: "URL parameters not found" }));
+ }
+ return;
case 'payments':
var poolBlocks = [];
for(var pool in portalStats.stats.pools) {
diff --git a/libs/paymentProcessor.js b/libs/paymentProcessor.js
index b804411b1..cd2e9003b 100644
--- a/libs/paymentProcessor.js
+++ b/libs/paymentProcessor.js
@@ -7,6 +7,9 @@ var async = require('async');
var Stratum = require('stratum-pool');
var util = require('stratum-pool/lib/util.js');
var CreateRedisClient = require('./createRedisClient.js');
+var WAValidator = require('wallet-address-validator');
+
+let badBlocks = {}
module.exports = function(logger){
@@ -742,39 +745,59 @@ function SetupForPool(logger, poolOptions, setupFinished){
// update confirmations for round
if (tx && tx.result)
round.confirmations = parseInt((tx.result.confirmations || 0));
-
+
// look for transaction errors
+ // NOTE: We should combine these two if blocks into one since the only difference is in the logged message.
if (tx.error && tx.error.code === -5){
- logger.warning(logSystem, logComponent, 'Daemon reports invalid transaction: ' + round.txHash);
- round.category = 'kicked';
+ if (undefined == badBlocks[round.txHash]) {
+ badBlocks[round.txHash] = 0
+ }
+
+ if (badBlocks[round.txHash] >= 15) {
+ logger.warning(logSystem, logComponent, 'ERROR: Daemon reports invalid transaction: ' + round.txHash)
+ delete badBlocks[round.txHash]
+ round.category = 'kicked'
+ } else {
+ badBlocks[round.txHash]++
+ logger.warning(logSystem, logComponent, `Abandoned block ${round.txHash} check ${badBlocks[round.txHash]}/15`)
+ }
return;
}
else if (!tx.result.details || (tx.result.details && tx.result.details.length === 0)){
- logger.warning(logSystem, logComponent, 'Daemon reports no details for transaction: ' + round.txHash);
- round.category = 'kicked';
- return;
- }
- else if (tx.error || !tx.result){
- logger.error(logSystem, logComponent, 'Odd error with gettransaction ' + round.txHash + ' ' + JSON.stringify(tx));
+ if (undefined == badBlocks[round.txHash]) {
+ badBlocks[round.txHash] = 0
+ }
+ if (badBlocks[round.txHash] >= 15) {
+ logger.warning(logSystem, logComponent, 'ERROR: Daemon reports no details for transaction: ' + round.txHash)
+ delete badBlocks[round.txHash]
+ round.category = 'kicked'
+ } else {
+ badBlocks[round.txHash]++
+ logger.warning(logSystem, logComponent, `Abandoned block ${round.txHash} check ${badBlocks[round.txHash]}/15`)
+ }
return;
}
+
// get the coin base generation tx
- var generationTx = tx.result.details.filter(function(tx){
- return tx.address === poolOptions.address;
- })[0];
- if (!generationTx && tx.result.details.length === 1){
- generationTx = tx.result.details[0];
+ var generationTx = tx.result.details.filter(tx => tx.address === poolOptions.address)[0]
+ if (!generationTx && tx.result.details.length === 1) {
+ generationTx = tx.result.details[0]
}
if (!generationTx){
- logger.error(logSystem, logComponent, 'Missing output details to pool address for transaction ' + round.txHash);
- return;
+ return logger.error(logSystem, logComponent, `ERROR: Missing output details to pool address for transaction ${round.txHash}`)
}
// get transaction category for round
- round.category = generationTx.category;
+ round.category = generationTx.category
// get reward for newly generated blocks
if (round.category === 'generate' || round.category === 'immature') {
round.reward = coinsRound(parseFloat(generationTx.amount || generationTx.value));
}
+
+ // Clear blocks that previously triggered an attempted kick.
+ if (!round.txHash in badBlocks) {
+ logger.error(logSystem, logComponent, `${round.txHash} is no longer bad!`)
+ delete badBlocks[round.txHash]
+ }
});
var canDeleteShares = function(r){
@@ -1417,11 +1440,15 @@ function SetupForPool(logger, poolOptions, setupFinished){
var getProperAddress = function(address){
- if (address.length >= 40){
- logger.warning(logSystem, logComponent, 'Invalid address '+address+', convert to address '+(poolOptions.invalidAddress || poolOptions.address));
- return (poolOptions.invalidAddress || poolOptions.address);
+ // Validation of Public and Identity addresses
+ var isvalid = WAValidator.validate(String(address).split(".")[0], 'VRSC');
+ if(isvalid !== true){
+/*
+ // Validation of sapling addreses (disabled until paymentProcessor.js can handle sapling payments)
+ var isvalid = WAValidator.validate(String(address).split(".")[0], 'VRSC', 'sapling');
}
- if (address.length <= 30) {
+ if (isvalid !== true){
+*/
logger.warning(logSystem, logComponent, 'Invalid address '+address+', convert to address '+(poolOptions.invalidAddress || poolOptions.address));
return (poolOptions.invalidAddress || poolOptions.address);
}
diff --git a/libs/poolWorker.js b/libs/poolWorker.js
index f6f19eca9..1bc0a22e9 100644
--- a/libs/poolWorker.js
+++ b/libs/poolWorker.js
@@ -5,6 +5,7 @@ var net = require('net');
var MposCompatibility = require('./mposCompatibility.js');
var ShareProcessor = require('./shareProcessor.js');
var CreateRedisClient = require('./createRedisClient.js');
+var WAValidator = require('wallet-address-validator');
module.exports = function(logger){
@@ -134,23 +135,23 @@ module.exports = function(logger){
var shareProcessor = new ShareProcessor(logger, poolOptions);
handlers.auth = function(port, workerName, password, authCallback){
- if (poolOptions.validateWorkerUsername !== true)
- authCallback(true);
- else {
- pool.daemon.cmd('validateaddress', [String(workerName).split(".")[0]], function (results) {
- var isValid = results.filter(function (r) {
- if (r.response)
- {
- return r.response.isvalid;
- }
- else
- {
- return false;
- }
- }).length > 0;
- authCallback(isValid);
- });
+ if (poolOptions.bannedAddresses.banned.indexOf(workerName) !== -1 && poolOptions.bannedAddresses.enabled == true) {
+ //Banned addresses return false if that option is enabled
+ isvalid = false;
+ } else if (poolOptions.validateWorkerUsername !== true) {
+ //Addresses are not checked for validity
+ isvalid = true;
+ } else {
+ //Validation of Public and Identity addresses
+ var isvalid = WAValidator.validate(String(workerName).split(".")[0], 'VRSC');
+/*
+ //Validation of sapling addreses (disabled until paymentProcessor.js can handle sapling payments)
+ if(isvalid !== true){
+ var isvalid = WAValidator.validate(String(address).split(".")[0], 'VRSC', 'sapling');
}
+*/
+ }
+ authCallback(isvalid);
};
handlers.share = function(isValidShare, isValidBlock, data){
diff --git a/libs/shareProcessor.js b/libs/shareProcessor.js
index c8727a2e9..7b54b2c9a 100644
--- a/libs/shareProcessor.js
+++ b/libs/shareProcessor.js
@@ -26,7 +26,7 @@ module.exports = function(logger, poolConfig){
var logSystem = 'Pool';
var logComponent = coin;
var logSubCat = 'Thread ' + (parseInt(forkId) + 1);
-
+
var connection = CreateRedisClient(redisConfig);
if (redisConfig.password) {
connection.auth(redisConfig.password);
@@ -69,11 +69,13 @@ module.exports = function(logger, poolConfig){
this.handleShare = function(isValidShare, isValidBlock, shareData) {
var redisCommands = [];
+ var dateNow = Date.now();
if (isValidShare) {
redisCommands.push(['hincrbyfloat', coin + ':shares:pbaasCurrent', shareData.worker, shareData.difficulty]);
redisCommands.push(['hincrbyfloat', coin + ':shares:roundCurrent', shareData.worker, shareData.difficulty]);
redisCommands.push(['hincrby', coin + ':stats', 'validShares', 1]);
+ redisCommands.push(['hset', coin + ':lastSeen', shareData.worker, dateNow]);
} else {
redisCommands.push(['hincrby', coin + ':stats', 'invalidShares', 1]);
}
@@ -81,7 +83,6 @@ module.exports = function(logger, poolConfig){
/* Stores share diff, worker, and unique value with a score that is the timestamp. Unique value ensures it
doesn't overwrite an existing entry, and timestamp as score lets us query shares from last X minutes to
generate hashrate for each worker and pool. */
- var dateNow = Date.now();
var hashrateData = [ isValidShare ? shareData.difficulty : -shareData.difficulty, shareData.worker, dateNow];
redisCommands.push(['zadd', coin + ':hashrate', dateNow / 1000 | 0, hashrateData.join(':')]);
diff --git a/libs/stats.js b/libs/stats.js
index 61040376f..5879bef99 100644
--- a/libs/stats.js
+++ b/libs/stats.js
@@ -244,7 +244,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
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+"*", "count", 1000, function(error, result) {
+ client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", "count", 50000, function(error, result) {
if (error) {
pcb(error);
return;
@@ -277,32 +277,32 @@ module.exports = function(logger, portalConfig, poolConfigs){
this.getBalanceByAddress = function(address, cback){
- var a = address.split(".")[0];
-
+ var a = address.split(".")[0];
+
var client = redisClients[0].client,
coins = redisClients[0].coins,
balances = [];
-
- var totalHeld = parseFloat(0);
- var totalPaid = parseFloat(0);
+
+ var totalHeld = parseFloat(0);
+ var totalPaid = parseFloat(0);
var totalImmature = parseFloat(0);
-
- async.each(_this.stats.pools, function(pool, pcb) {
- var coin = String(_this.stats.pools[pool.name].name);
- // get all immature balances from address
- client.hscan(coin + ':immature', 0, "match", a+"*", "count", 10000, function(error, pends) {
+
+ async.each(_this.stats.pools, function(pool, pcb) {
+ var coin = String(_this.stats.pools[pool.name].name);
+ // get all immature balances from address
+ client.hscan(coin + ':immature', 0, "match", a+"*", "count", 50000, function(error, pends) {
// 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", 50000, function(error, bals) {
// 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", 50000, function(error, pays) {
+
var workerName = "";
var balAmount = 0;
var paidAmount = 0;
var pendingAmount = 0;
-
+
var workers = {};
-
+
for (var i in pays[1]) {
if (Math.abs(i % 2) != 1) {
workerName = String(pays[1][i]);
@@ -329,11 +329,11 @@ module.exports = function(logger, portalConfig, poolConfigs){
workers[workerName] = (workers[workerName] || {});
} else {
pendingAmount = parseFloat(pends[1][b]);
- workers[workerName].immature = coinsRound(pendingAmount);
+ workers[workerName].immature = coinsRound(satoshisToCoins(pendingAmount));
totalImmature += pendingAmount;
}
}
-
+
for (var w in workers) {
balances.push({
worker:String(w),
@@ -342,23 +342,95 @@ module.exports = function(logger, portalConfig, poolConfigs){
immature:workers[w].immature
});
}
-
+
pcb();
});
});
});
- }, function(err) {
- if (err) {
- callback("There was an error getting balances");
- return;
- }
-
- _this.stats.balances = balances;
- _this.stats.address = address;
+ }, function(err) {
+ if (err) {
+ callback("There was an error getting balances");
+ return;
+ }
- 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.getPoolBalancesByAddress = function(address, callback){
+ var a = address.split(".")[0];
+
+ var client = redisClients[0].client,
+ coins = redisClients[0].coins,
+ poolBalances = [];
+
+ async.each(_this.stats.pools, function(pool, pcb) {
+ var poolName = pool.name;
+ var coin = String(poolName);
+
+ // get all immature balances from address
+ client.hscan(coin + ':immature', 0, "match", a + "*", "count", 50000, function(error, pends) {
+ // get all balances from address
+ client.hscan(coin + ':balances', 0, "match", a + "*", "count", 50000, function(error, bals) {
+ // get all payouts from address
+ client.hscan(coin + ':payouts', 0, "match", a + "*", "count", 50000, function(error, pays) {
+
+ var workers = {};
+
+ // Process payouts
+ for (var i = 0; i < pays[1].length; i += 2) {
+ var workerName = String(pays[1][i]);
+ var paidAmount = parseFloat(pays[1][i + 1]);
+
+ workers[workerName] = workers[workerName] || {};
+ workers[workerName].paid = coinsRound(paidAmount);
+ }
+
+ // Process balances
+ for (var j = 0; j < bals[1].length; j += 2) {
+ var workerName = String(bals[1][j]);
+ var balAmount = parseFloat(bals[1][j + 1]);
+
+ workers[workerName] = workers[workerName] || {};
+ workers[workerName].balance = coinsRound(balAmount);
+ }
+
+ // Process immature balances
+ for (var k = 0; k < pends[1].length; k += 2) {
+ var workerName = String(pends[1][k]);
+ var pendingAmount = parseFloat(pends[1][k + 1]);
+
+ workers[workerName] = workers[workerName] || {};
+ workers[workerName].immature = coinsRound(pendingAmount);
+ }
+
+ // Push balances for each worker to the poolBalances array
+ for (var worker in workers) {
+ poolBalances.push({
+ pool: poolName,
+ worker: worker,
+ balance: workers[worker].balance || 0,
+ paid: workers[worker].paid || 0,
+ immature: workers[worker].immature || 0
+ });
+ }
+
+ pcb();
+ });
+ });
+ });
+ }, function(err) {
+ if (err) {
+ callback("There was an error getting balances");
+ return;
+ }
+
+ callback(poolBalances);
+ });
+ };
this.getGlobalStats = function(callback){
diff --git a/libs/website.js b/libs/website.js
index c64882194..e38e99fc1 100644
--- a/libs/website.js
+++ b/libs/website.js
@@ -134,7 +134,7 @@ module.exports = function(logger){
var buildKeyScriptPage = function(){
async.waterfall([
function(callback){
- var client = CreateRedisClient(portalConfig);
+ var client = CreateRedisClient(portalConfig.redis);
if (portalConfig.redis.password) {
client.auth(portalConfig.redis.password);
}
diff --git a/package-lock.json b/package-lock.json
index 72f3f6490..748e8bb1b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -440,7 +440,7 @@
"bigi": "^1.4.0",
"bip66": "^1.1.0",
"bitcoin-ops": "^1.3.0",
- "blake2b": "git+https://github.com/BitGo/blake2b.git#6268e6dd678661e0acc4359e9171b97eb1ebf8ac",
+ "blake2b": "git+https://github.com/Oink70/blake2b.git#3eaf87bc21da3eebc10a6e183bc237f1f44de9b0",
"bs58check": "^2.0.0",
"create-hash": "^1.1.0",
"create-hmac": "^1.1.3",
@@ -475,16 +475,16 @@
}
},
"blake2b": {
- "version": "git+https://github.com/BitGo/blake2b.git#6268e6dd678661e0acc4359e9171b97eb1ebf8ac",
- "from": "git+https://github.com/BitGo/blake2b.git#6268e6dd678661e0acc4359e9171b97eb1ebf8ac",
+ "version": "git+https://github.com/Oink70/blake2b.git#3eaf87bc21da3eebc10a6e183bc237f1f44de9b0",
+ "from": "git+https://github.com/Oink70/blake2b.git#3eaf87bc21da3eebc10a6e183bc237f1f44de9b0",
"requires": {
- "blake2b-wasm": "git+https://github.com/BitGo/blake2b-wasm.git#193cdb71656c1a6c7f89b05d0327bb9b758d071b",
+ "blake2b-wasm": "git+https://github.com/Oink70/blake2b-wasm.git#c183fabcb22cf84d463c7b10f25877a8e4afdc6d",
"nanoassert": "^1.0.0"
}
},
"blake2b-wasm": {
- "version": "git+https://github.com/BitGo/blake2b-wasm.git#193cdb71656c1a6c7f89b05d0327bb9b758d071b",
- "from": "git+https://github.com/BitGo/blake2b-wasm.git#193cdb71656c1a6c7f89b05d0327bb9b758d071b",
+ "version": "git+https://github.com/Oink70/blake2b-wasm.git#c183fabcb22cf84d463c7b10f25877a8e4afdc6d",
+ "from": "git+https://github.com/Oink70/blake2b-wasm.git#c183fabcb22cf84d463c7b10f25877a8e4afdc6d",
"requires": {
"nanoassert": "^1.0.0"
}
@@ -1020,11 +1020,6 @@
"resolved": "https://registry.npmjs.org/dot/-/dot-1.1.2.tgz",
"integrity": "sha1-xzdwGfxOVQeYkosrmv62ar+h8vk="
},
- "double-ended-queue": {
- "version": "2.1.0-0",
- "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
- "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
- },
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -1071,8 +1066,8 @@
}
},
"equihashverify": {
- "version": "git+https://github.com/s-nomp/equihashverify.git#2e9ca0742220c405be71efa2468bcfda0a5075f9",
- "from": "git+https://github.com/s-nomp/equihashverify.git",
+ "version": "git+https://github.com/Oink70/equihashverify.git#2b2c5c9725f1e85a4eaa8b1c8f974aad65362c8e",
+ "from": "git+https://github.com/Oink70/equihashverify.git",
"requires": {
"bindings": "*",
"libsodium": "^0.7.3",
@@ -3084,24 +3079,35 @@
}
},
"redis": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
- "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
+ "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
"requires": {
- "double-ended-queue": "^2.1.0-0",
- "redis-commands": "^1.2.0",
- "redis-parser": "^2.6.0"
+ "denque": "^1.5.0",
+ "redis-commands": "^1.7.0",
+ "redis-errors": "^1.2.0",
+ "redis-parser": "^3.0.0"
}
},
+ "denque": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
+ "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
+ },
+ "redis-errors": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
+ "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="
+ },
"redis-commands": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz",
- "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA=="
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
+ "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"redis-parser": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
- "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
+ "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="
},
"regex-not": {
"version": "1.0.2",
@@ -3524,17 +3530,17 @@
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"stratum-pool": {
- "version": "git+https://github.com/miketout/node-stratum-pool.git#c6e35910eb67a98f2315cb0d6a2d1b5eda639b85",
- "from": "git+https://github.com/miketout/node-stratum-pool.git",
+ "version": "git+https://github.com/VerusCoin/node-stratum-pool.git#5a45e655b8d170253e1fbd56efbf85dd26d8347b",
+ "from": "git+https://github.com/VerusCoin/node-stratum-pool.git",
"requires": {
"async": "^2.6.1",
"base58-native": "^0.1.4",
"bignum": "^0.13.0",
"bitgo-utxo-lib": "git+https://github.com/miketout/bitgo-utxo-lib.git#4649a79ffc55891e4ad40ac68793befa7348ee6a",
- "equihashverify": "git+https://github.com/s-nomp/equihashverify.git#2e9ca0742220c405be71efa2468bcfda0a5075f9",
+ "equihashverify": "git+https://github.com/Oink70/equihashverify.git#2b2c5c9725f1e85a4eaa8b1c8f974aad65362c8e",
"merkle-bitcoin": "^1.0.2",
"promise": "^8.0.1",
- "verushash": "git+https://github.com/VerusCoin/verushash-node.git#e6572f345b8c24e4a1212f6c9eb66c1c400d83e1"
+ "verushash": "git+https://github.com/VerusCoin/verushash-node.git#fd4d5fb9e7eefa34d70277bd5cc672bc472f821c"
}
},
"string-width": {
@@ -3849,7 +3855,7 @@
}
},
"verushash": {
- "version": "git+https://github.com/VerusCoin/verushash-node.git#e6572f345b8c24e4a1212f6c9eb66c1c400d83e1",
+ "version": "git+https://github.com/VerusCoin/verushash-node.git#fd4d5fb9e7eefa34d70277bd5cc672bc472f821c",
"from": "git+https://github.com/VerusCoin/verushash-node.git",
"requires": {
"bindings": "*",
@@ -3945,6 +3951,35 @@
"argparse": "^1.0.7",
"glob": "^7.0.5"
}
+ },
+ "wallet-address-validator": {
+ "version": "git+https://github.com/Oink70/wallet-address-validator.git#cf7835d1a569eed6fa98dd3d4aa4cd287bb59623",
+ "from": "git+https://github.com/Oink70/wallet-address-validator.git#cf7835d1a569eed6fa98dd3d4aa4cd287bb59623",
+ "dependencies": {
+ "base-x": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.4.tgz",
+ "integrity": "sha512-UYOadoSIkEI/VrRGSG6qp93rp2WdokiAiNYDfGW5qURAY8GiAQkvMbwNNSDYiVJopqv4gCna7xqf4rrNGp+5AA==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jssha": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.3.1.tgz",
+ "integrity": "sha1-FHshJTaQNcpLL30hDcU58Amz3po="
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ }
+ }
+ },
+ "jssha": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.3.1.tgz",
+ "integrity": "sha1-FHshJTaQNcpLL30hDcU58Amz3po="
}
}
}
diff --git a/package.json b/package.json
index 947142974..332e5efb5 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"dependencies": {
"async": "^2.6.1",
"bignum": "^0.13.0",
- "bitgo-utxo-lib": "git+https://github.com/VerusCoin/bitgo-utxo-lib.git",
+ "bitgo-utxo-lib": "git+https://github.com/Oink70/bitgo-utxo-lib.git",
"body-parser": "^1.18.3",
"colors": "^1.3.2",
"compression": "^1.7.3",
@@ -48,12 +48,14 @@
"node-watch": "^0.5.8",
"nonce": "^1.0.4",
"pm2": "^3.2.2",
- "redis": "^2.8.0",
+ "redis": "^3.0.0",
"request": "^2.88.0",
- "stratum-pool": "git+https://github.com/VerusCoin/node-stratum-pool.git"
+ "stratum-pool": "git+https://github.com/Oink70/node-stratum-pool.git",
+ "wallet-address-validator": "git+https://github.com/Oink70/wallet-address-validator.git#cf7835d1a569eed6fa98dd3d4aa4cd287bb59623",
+ "jssha": "^2.3.1"
},
"engines": {
- "node": ">=8.11"
+ "node": "10"
},
"scripts": {
"start": "NODE_PATH=$NODE_PATH:$PWD/node_modules/verushash/build/Release/:$PWD/node_modules/stratum-pool/node_modules/verushash/build/Release/ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/node_modules/stratum-pool/node_modules/equihashverify/build/Release/:$PWD/node_modules/equihashverify/build/Release/:$PWD/node_modules/verushash/build/Release/:$PWD/node_modules/stratum-pool/node_modules/verushash/build/Release/ node init.js"
diff --git a/pool_configs/examples/vrsc.json b/pool_configs/examples/vrsc.json
index af3fd906b..99f34c046 100644
--- a/pool_configs/examples/vrsc.json
+++ b/pool_configs/examples/vrsc.json
@@ -35,7 +35,7 @@
"paymentMode": "prop",
"_comment_paymentMode":"prop, pplnt",
"pplnt": 51,
- "_comment_pplnt": "If pplnt is active and clients have mined less that this part, their shares are slashed."
+ "_comment_pplnt": "If pplnt is active and clients have mined less that this part, their shares are slashed.",
"paymentInterval": 120,
"_comment_paymentInterval": "Interval in seconds to check and perform payments.",
"minimumPayment": 1,
diff --git a/scripts/pbaascheck.sh b/scripts/pbaascheck.sh
new file mode 100755
index 000000000..351ac7356
--- /dev/null
+++ b/scripts/pbaascheck.sh
@@ -0,0 +1,276 @@
+#!/bin/bash
+##
+## © verus.io 2018-2024, released under MIT license
+## Script written in 2023 by Oink.vrsc@
+## Script maintained by Oink.vrsc@
+
+# check if script is already running
+if [ -f /tmp/pbaascheck.pid ]
+then
+ PID_TIME=$(stat -c '%W' /tmp/pbaascheck.pid)
+ CUR_TIME=$(date +%s)
+ PID_AGE=$(echo "$CUR_TIME - $PID_TIME" | bc)
+ if [[ $PID_AGE -le 3600 ]]
+ then
+ echo "script is already running"
+ exit 1
+ else
+ echo "script has apparently aborted before removing /tmp/pbaascheck.pid, continuing"
+ fi
+else
+ touch /tmp/pbaascheck.pid
+fi
+
+## default settings (change where needed)
+PAYMENT=/home/pool/payment # Change if you used a different location
+VERUS=/home/verus/bin/verus # complete path to (and including) the verus RPC client
+MAIN_CHAIN=VRSC # main hashing chain
+REDIS_NAME=verus # name you assigned the coin in `/home/pool/s-nomp/coins/*.json`
+REDIS_HOST=127.0.0.1 # If you run this script on another system, alter the IP address of your Redis server
+REDIS_PORT=6379 # If you use a different REDIS port, alter the port accordingly
+
+## Set script folder
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
+
+## check if the Verus binary is found.
+## If Verus exists in the PATH environment, use it.
+## If not, fall back to predefined location in this script.
+if ! command -v verus &>/dev/null
+then
+ echo "verus not found in your PATH environment. Using location from line 9 in this script."
+ if ! command -v $VERUS &>/dev/null
+ then
+ echo "Verus could not be found. Make sure it's in your path and/or in line 9 of this script."
+ echo "exiting..."
+ exit 1
+ fi
+else
+ VERUS=$(which verus)
+fi
+
+
+## Dependencies: bc, jq, tr, cut, redis-cli/keydb-cli
+## bc
+if ! command -v bc &>/dev/null ; then
+ echo "jq not found. please install using your package manager."
+ exit 1
+else
+ BC=$(which bc)
+fi
+## jq
+if ! command -v jq &>/dev/null ; then
+ echo "jq not found. please install using your package manager."
+ exit 1
+else
+ JQ=$(which jq)
+fi
+## tr
+if ! command -v tr &>/dev/null ; then
+ echo "tr not found. please install using your package manager."
+ exit 1
+else
+ TR=$(which tr)
+fi
+## cut
+if ! command -v cut &>/dev/null ; then
+ echo "cut not found. please install using your package manager."
+ exit 1
+else
+ CUT=$(which cut)
+fi
+## redis-cli and/or keydb-cli
+if ! command -v redis-cli &>/dev/null ; then
+ if ! command -v keydb-cli &>/dev/null ; then
+ echo "Both redis-cli or keydb-cli not found. Please install one using your package manager."
+ exit 1
+ fi
+ REDIS_CLI="$(which keydb-cli) -h $REDIS_HOST -p $REDIS_PORT"
+else
+ REDIS_CLI="$(which redis-cli) -h $REDIS_HOST -p $REDIS_PORT"
+fi
+
+## Can we connect to Redis?
+if [[ "$($REDIS_CLI ping)" != "PONG" ]]
+then
+ echo "cannot connect to redis server"
+ exit 1
+fi
+
+## Is main chain active?
+count=$(${VERUS} -chain=$MAIN_CHAIN getconnectioncount 2>/dev/null)
+case $count in
+ ''|*[!0-9]*) DAEMON_ACTIVE=0 ;;
+ *) DAEMON_ACTIVE=1 ;;
+esac
+if [[ "$DAEMON_ACTIVE" != "1" ]]
+then
+ echo "$MAIN_CHAIN daemon is not running and connected. Start your $MAIN_CHAIN daemon and wait for it to be connected."
+ exit 1
+fi
+
+## Return a list of found PBaaS hashes:
+HASHLIST=$($REDIS_CLI smembers $REDIS_NAME:pbaasPending | $CUT -d' ' -f2-)
+## return a list on found shares per miner
+SHARELIST=$($REDIS_CLI hgetall $REDIS_NAME:shares:pbaasCurrent| $CUT -d' ' -f2-)
+## get list of active chains in ecosystem
+PBAAS_CHAINS=$($VERUS -chain=$MAIN_CHAIN listcurrencies '{"systemtype":"pbaas"}' | jq --arg MAIN_CHAIN "${MAIN_CHAIN}" -r '.[].currencydefinition | select (.name != "$MAIN_CHAIN") | .name')
+## determine chains active on this system
+for i in $PBAAS_CHAINS
+do
+ count=$(${VERUS} -chain=$i getconnectioncount 2>/dev/null)
+ case $count in
+ ''|*[!0-9]*) DAEMON_ACTIVE=0 ;;
+ *) DAEMON_ACTIVE=1 ;;
+ esac
+ if [[ "$DAEMON_ACTIVE" = "1" ]]
+ then
+ ACTIVE_CHAINS="$ACTIVE_CHAINS $i"
+ fi
+done
+ACTIVE_CHAINS=$(echo $ACTIVE_CHAINS | sed 's/VRSC//g');
+
+## Check each hash on all chains
+for i in $HASHLIST
+do
+ for j in $ACTIVE_CHAINS
+ ## put in break for non-running chains
+ do
+ CHECK=$($VERUS -chain=$j getblock $(echo $i | $CUT -d':' -f1) 2 2>/dev/null)
+ if [[ "$CHECK" =~ "$(echo $i | cut -d':' -f1)" ]]
+ then
+ TRANSACTION=$(echo "$CHECK" | $JQ -r '.tx[0].txid')
+ BLOCK=$(echo "$CHECK" | $JQ '.height')
+ echo "$j contains blockhash $(echo $i | cut -d':' -f1), TXID: $TRANSACTION"
+ REDIS_NEW_PENDING="${i:0:65}"$TRANSACTION:$BLOCK:"${i:65}"
+ # do not insert data for the main mining chain
+ if [[ "$(echo $j | $TR '[:upper:]' '[:lower:]')" != "$(echo $MAIN_CHAIN | $TR '[:upper:]' '[:lower:]')" ]]
+ then
+ $REDIS_CLI sadd $(echo $j | $TR '[:upper:]' '[:lower:]'):blocksPending $REDIS_NEW_PENDING 1>/dev/null
+ ## if no shares are known for this round yet, add them
+ SHARES_AVAILABLE="$($REDIS_CLI hgetall $(echo $j | tr '[:upper:]' '[:lower:]'):shares:round$BLOCK)"
+ if [[ "$SHARES_AVAILABLE" == "" ]]
+ then
+ $REDIS_CLI hset $(echo $j | tr '[:upper:]' '[:lower:]'):shares:round$BLOCK $SHARELIST 1>/dev/null
+ fi
+ fi
+ fi
+ done
+done
+
+# Temporarily store unknown blockhashes in the script folder
+# ToDo: Needs storing to Redis
+UNKNOWN_HASHLIST=$($REDIS_CLI smembers $REDIS_NAME:pbaasPending | $CUT -d' ' -f2-)
+if [ -f $SCRIPT_DIR/unknown_hashlist.4 ]
+then
+ while read -r LINE
+ do
+ if [[ "$UNKNOWN_HASHLIST" == *"$LINE"* ]]
+ then
+ echo "removing $LINE from REDIS"
+ $REDIS_CLI srem $REDIS_NAME:pbaasPending $LINE 1>/dev/null
+ fi
+ done < $SCRIPT_DIR/unknown_hashlist.4
+ rm $SCRIPT_DIR/unknown_hashlist.4
+fi
+
+for i in {3..1}
+do
+ if [ -f $SCRIPT_DIR/unknown_hashlist.$i ]
+ then
+ mv $SCRIPT_DIR/unknown_hashlist.$i $SCRIPT_DIR/unknown_hashlist.$((i+1))
+ fi
+done
+
+for i in $UNKNOWN_HASHLIST
+do
+ echo $i >> $SCRIPT_DIR/unknown_hashlist.1
+done
+
+for CHAIN in $ACTIVE_CHAINS
+do
+ CHAINlc=$(echo $(echo $CHAIN | $TR '[:upper:]' '[:lower:]'))
+ INVALIDADDRESS=$(cat $PAYMENT/pool_configs/$CHAINlc.json | jq -r .invalidAddress)
+ echo "Processing i-addresses on $CHAIN. $INVALIDADDRESS used for nonexisting IDs"
+ ALL_ADDRESSES=$($REDIS_CLI HSCAN $CHAINlc:balances 0 COUNT 50000 | awk '{print $1}' | sed -n 'n;p' | sed 's/\..*//' | grep -e "^i.*" | sort | uniq)
+ while read -r ADDRESS; do
+ if [[ $ADDRESS == i* ]]
+ then
+ I_ADDRESS=$ADDRESS
+ BALANCES=
+ DONATIONS=
+ if [[ $($VERUS -chain=$CHAIN getidentity "$I_ADDRESS") ]]
+ then
+ echo "$I_ADDRESS exists on vARRR, no action needed."
+ else
+ ## Retrieve ID info from mainchain
+ ID_MAINCHAIN=$($VERUS getidentity "$I_ADDRESS")
+ # check if the ID exists on the main chain
+ if [[ $(echo $IDMAINCHAIN) == error* ]]
+ then
+ break # testing WTF is happening.
+ # Collect all address.worker entries for the i-address for move to donation address
+ echo "$I_ADDRESS balance is a donation on $CHAIN..."
+ DONATION_TMP=$($REDIS_CLI hscan $CHAINlc:balances 0 COUNT 40000 MATCH $ADDRESS* | awk 'NR % 2 == 0')
+ if ! [ "$DONATION_TMP" == "" ]
+ then
+ DONATIONS=$DONATIONS$DONATION_TMP" "
+ while read OLD_ADDRESS
+ do
+ BALANCE=0
+ NEW_ADDRESS=0
+ tmp=(${OLD_ADDRESS//./ })
+ addr=${tmp[0]}
+ NEW_ADDRESS=$(echo $OLD_ADDRESS | sed "s/${addr}/${INVALIDADDRESS}/g")
+ BALANCE=$($REDIS_CLI HGET $CHAINlc:balances $OLD_ADDRESS)
+ $REDIS_CLI HDEL $CHAINlc:balances $OLD_ADDRESS
+ $REDIS_CLI HINCBYFLOAT $CHAINlc:balances $NEW_ADDRESS $BALANCE
+ done <<<$DONATIONS
+ fi
+ else
+ # Collect all address.worker entries for the i-address for move to R-address
+ BALANCES_TMP=$($REDIS_CLI hscan $CHAINlc:balances 0 COUNT 40000 MATCH $ADDRESS* | awk 'NR % 2 == 0')
+ if ! [ "$BALANCES_TMP" == "" ]
+ then
+ BALANCES=$BALANCES$BALANCES_TMP" "
+ R_ADDRESS=$(echo $ID_MAINCHAIN | jq -r .identity.primaryaddresses[0])
+ while read OLD_ADDRESS
+ do
+ tmp=(${OLD_ADDRESS//./ })
+ addr=${tmp[0]}
+ NEW_ADDRESS=$(echo $OLD_ADDRESS | sed "s/${addr}/${R_ADDRESS}/g")
+ BALANCE=$($REDIS_CLI HGET $CHAINlc:balances $OLD_ADDRESS)
+ $REDIS_CLI HDEL $CHAINlc:balances $OLD_ADDRESS
+ $REDIS_CLI HINCRBYFLOAT $CHAINlc:balances $NEW_ADDRESS $BALANCE
+ done <<<$BALANCES
+ fi
+ fi
+ fi
+ fi
+ done<<<$ALL_ADDRESSES
+done
+
+WORKERSHAREREDUCTION=
+## Retrieve data from REDIS
+WORKERSHARES=$($REDIS_CLI HGETALL $REDIS_NAME:shares:pbaasCurrent)
+
+## if the value of shares is below two, remove the key from the database, otherwise substract 50% of the value
+for LINE in $WORKERSHARES
+do
+ if [[ "$LINE" =~ ^[0-9] ]]
+ then
+ if [[ "$LINE" < "2.00000000" ]]
+ then
+ $REDIS_CLI HDEL $REDIS_NAME:shares:pbaasCurrent "$WORKERSHAREREDUCTION"
+ else
+ REDUCTION=$(echo "scale=8;$LINE / 2" | $BC)
+ $REDIS_CLI HINCRBYFLOAT $REDIS_NAME:shares:pbaasCurrent "$WORKERSHAREREDUCTION" "-$REDUCTION"
+ fi
+ WORKERSHAREREDUCTION=
+ else
+ WORKERSHAREREDUCTION=$LINE
+ fi
+done <<<$WORKERSHARES
+
+rm /tmp/pbaascheck.pid
+
+#EOF
diff --git a/website/pages/api.html b/website/pages/api.html
index 1fdb5889a..719bc9fa9 100644
--- a/website/pages/api.html
+++ b/website/pages/api.html
@@ -7,7 +7,9 @@
/pool_stats - historical stats
/payments - payment history
/worker_stats?taddr - historical time per pool json
+/worker_balances?taddr - balance per pool json
/live_stats - live stats
+
diff --git a/website/pages/miner_stats.html b/website/pages/miner_stats.html
index 5349f9b97..0fd65c13b 100644
--- a/website/pages/miner_stats.html
+++ b/website/pages/miner_stats.html
@@ -1,9 +1,9 @@
-
-
{{=String(it.stats.address).split(".")[0]}}
-
... (Avg)
-
... (Now)
-
Luck ... Days
-
-
-
-
Shares: ...
-
Immature: ...
-
Bal: ...
-
Paid: ...
-
+
+
{{=String(it.stats.address).split(".")[0]}}
+
... (Avg)
+
... (Now)
+
Luck ... Days
+
+
+
+
+
+
+
diff --git a/website/pages/payments.html b/website/pages/payments.html
index 579d2f17a..bab582f61 100644
--- a/website/pages/payments.html
+++ b/website/pages/payments.html
@@ -66,6 +66,7 @@
{{ for(var p in it.stats.pools[pool].payments) { }}
+ {{=it.stats.pools[pool].symbol}} payments
|
{{ if (it.poolsConfigs[pool].coin.explorer && it.poolsConfigs[pool].coin.explorer.txURL) { }}
@@ -74,7 +75,8 @@
{{=it.stats.pools[pool].payments[p].blocks}}
{{ } }}
|
- {{=readableDate(it.stats.pools[pool].payments[p].time)}} |
+
+ |
{{=it.stats.pools[pool].payments[p].miners}} |
{{=Math.round(it.stats.pools[pool].payments[p].shares)}} |
{{=it.stats.pools[pool].payments[p].paid}} {{=it.stats.pools[pool].symbol}} |
diff --git a/website/pages/stats.html b/website/pages/stats.html
index a0f92eea6..3683b6b69 100644
--- a/website/pages/stats.html
+++ b/website/pages/stats.html
@@ -149,7 +149,9 @@
{{=block[2]}}
{{ } }}
{{if (block[4] != null) { }}
- {{=readableDate(block[4])}}
+
+
+
{{ } }}
{{if (it.stats.pools[pool].pending.confirms) { }}
{{if (it.stats.pools[pool].pending.confirms[block[0]]) { }}
@@ -175,7 +177,9 @@
{{=block[2]}}
{{ } }}
{{if (block[4] != null) { }}
- {{=readableDate(block[4])}}
+
+
+
{{ } }}
*CREDITED*
diff --git a/website/pages/workers.html b/website/pages/workers.html
index 27f6378f4..f28fd68d3 100644
--- a/website/pages/workers.html
+++ b/website/pages/workers.html
@@ -52,43 +52,45 @@
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }}
{{ var i=0; for(var pool in it.stats.pools) { }}
-
-
-
-
- Miner Lookup:
-
-
-
-
-
-
- {{=capitalizeFirstLetter(it.stats.pools[pool].name)}} Top Miners
- {{=it.stats.pools[pool].minerCount}} Miners
- {{=it.stats.pools[pool].workerCount}} Workers
- {{=it.stats.pools[pool].shareCount}} Shares
-
-
-
-
-
- | Address |
- Shares |
- Efficiency |
- Hashrate |
-
-
- {{ for(var worker in it.stats.pools[pool].miners) { }}
- {{var workerstat = it.stats.pools[pool].miners[worker];}}
-
- | {{=worker}} |
- {{=Math.round(workerstat.currRoundShares * 100) / 100}} |
- {{? workerstat.shares > 0}} {{=Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100}}% {{??}} 0% {{?}} |
- {{=workerstat.hashrateString}} |
+{{ if (it.stats.pools[pool].minerCount > 0) { }}
+
+
+
+
+ Miner Lookup:
+
+
+
+
+
+
+ {{=capitalizeFirstLetter(it.stats.pools[pool].name)}} Top Miners
+ {{=it.stats.pools[pool].minerCount}} Miners
+ {{=it.stats.pools[pool].workerCount}} Workers
+ {{=it.stats.pools[pool].shareCount}} Shares
+
+
+
+
+
+ | Address |
+ Shares |
+ Efficiency |
+ Hashrate |
- {{ } }}
-
+
+ {{ for(var worker in it.stats.pools[pool].miners) { }}
+ {{var workerstat = it.stats.pools[pool].miners[worker];}}
+
+ | {{=worker}} |
+ {{=Math.round(workerstat.currRoundShares * 100) / 100}} |
+ {{? workerstat.shares > 0}} {{=Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100}}% {{??}} 0% {{?}} |
+ {{=workerstat.hashrateString}} |
+
+ {{ } }}
+
+
-
+{{ } }}
{{ } }}