Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
827cf61
Dates in local time
Oink70 Jun 8, 2023
6fb2540
Update payments.html
Oink70 Jun 8, 2023
ca38bdc
Merge pull request #1 from Oink70/Date-and-time-processing
Oink70 Jun 8, 2023
141d246
dely kicking unknown blocks by 15 blocks
Oink70 Jun 13, 2023
4e31bc0
Update dependencies (BitGo/blake2b, BitGo/blake2bs-wasm and s-nomp/eq…
Oink70 Jun 16, 2023
ab74607
website fix, where S-NOMP fails to start with non-local/non-default d…
Oink70 Jun 16, 2023
a2afec1
rectify variable decleration on : delay kicking
Oink70 Jun 17, 2023
80814c2
Create dependency-review.yml
Oink70 Jul 4, 2023
3e5cb4a
Store LastSeen timestamp for workers in Redis
Oink70 Sep 1, 2023
6583027
Use Redis lastSeen in delta time calculation
Oink70 Sep 1, 2023
7a3351d
Updated Redis dependencies
Oink70 Sep 1, 2023
e986685
declare time BEFORE <Store LastSeen timestamp for workers in Redis>
Oink70 Sep 1, 2023
d345e5c
Update package-lock.json
Oink70 Sep 2, 2023
ba64150
Address validation done by JS instead of daemon
Oink70 Feb 29, 2024
7dfcd1d
Merge pull request #2 from Oink70/address-validation
Oink70 Feb 29, 2024
74bc900
Add script for PBaaS chains
Oink70 Mar 18, 2024
bdb0f05
Merge pull request #3 from Oink70/address-validation
Oink70 Mar 19, 2024
55d7f1e
Do not duplicate into main chain stats
Oink70 Mar 19, 2024
788a7e7
add in port and host variables
Oink70 Mar 19, 2024
0bddbb1
Added database connection check
Oink70 Mar 19, 2024
9178734
Copyright notice change
Oink70 Mar 19, 2024
385a395
Update paymentProcessor.js
Oink70 Mar 24, 2024
b0fb473
use pid file to prevent script running if alreeady active
Oink70 Apr 1, 2024
9f0cda8
convert non-payable i-addresses to primary adresses of their VRSC IDs
Oink70 Apr 4, 2024
5fe6901
Get replacement address from s-nomp config for invalid i-addresses
Oink70 Apr 5, 2024
72ec3c2
Include Redispass and share management
Oink70 Apr 6, 2024
192d375
Update pbaascheck.sh
Oink70 Apr 6, 2024
93a258e
Payment instance location is now a variable
Oink70 Apr 7, 2024
a2d8c88
Update vrsc.json
Oink70 Apr 11, 2024
3f18ddc
raise 1000 worker limit to 50k when counting shares.
Oink70 Apr 11, 2024
79b4465
Add in api call worker_balances
Oink70 Apr 11, 2024
ef4dbf3
show all balances on the miner statictics pages
Oink70 Apr 11, 2024
f85048a
convert satoshi to coins for immatire balances
Oink70 Apr 11, 2024
be08602
Only show pools with active workers
Oink70 Apr 11, 2024
f36a126
Mention worker_balances api opn the api page.
Oink70 Apr 11, 2024
127ff03
Resolve lack /api/blocks
uncharted9898 Apr 12, 2024
8f9c1f9
Merge pull request #5 from uncharted9898/patch-2
Oink70 Apr 15, 2024
88446bb
show coin on payments page
Oink70 Apr 22, 2024
3eca5cb
Avoid script not firing if PID file doesn't get cleared for any reason
Oink70 Apr 30, 2024
a5d3fcb
remove unnecessary database copy and use correct share key
Oink70 Apr 30, 2024
c1aa1de
Update pbaascheck.sh
Oink70 May 1, 2024
23a2026
Add option for address banning
Oink70 Sep 8, 2024
315be63
Update package.json
Oink70 Oct 28, 2025
42a0c09
Update package.json
Oink70 Oct 28, 2025
de50d56
Update package.json
Oink70 Oct 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions config_example.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
5 changes: 3 additions & 2 deletions init.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 :)
Expand Down
53 changes: 53 additions & 0 deletions libs/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
69 changes: 48 additions & 21 deletions libs/paymentProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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){

Expand Down Expand Up @@ -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){
Expand Down Expand Up @@ -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);
}
Expand Down
33 changes: 17 additions & 16 deletions libs/poolWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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){

Expand Down Expand Up @@ -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){
Expand Down
5 changes: 3 additions & 2 deletions libs/shareProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -69,19 +69,20 @@ 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]);
}

/* 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(':')]);

Expand Down
Loading