Files
peya-nodejs-pool/lib/utils.js

324 lines
9.4 KiB
JavaScript
Raw Normal View History

/**
* Cryptonote Node.JS Pool
* https://github.com/dvandal/cryptonote-nodejs-pool
*
* Utilities functions
**/
// Load required module
let crypto = require('crypto');
let dateFormat = require('dateformat');
exports.dateFormat = dateFormat;
let cnUtil = require('cryptoforknote-util');
exports.cnUtil = cnUtil;
require('./configReader.js');
// Initialize log system
let logSystem = 'pool';
require('./exceptionWriter.js')(logSystem);
let threadId = '(Thread ' + process.env.forkId + ') ';
let log = function (severity, system, text, data) {
global.log(severity, system, threadId + text, data);
};
/**
* Generate random instance id
**/
exports.instanceId = function () {
return crypto.randomBytes(4);
}
/**
* Validate miner address
**/
var addressBase58Prefix = config.poolServer.pubAddressPrefix ? parseInt(config.poolServer.pubAddressPrefix) : parseInt(cnUtil.address_decode(Buffer.from(config.poolServer.poolAddress)).toString());
let integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1;
let subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A";
// Get address prefix
function getAddressPrefix (address) {
2021-01-19 22:49:31 -05:00
let addressBuffer = Buffer.from(address);
let addressPrefix = cnUtil.address_decode(addressBuffer);
2021-01-04 12:56:28 -05:00
if (addressPrefix) {
addressPrefix = parseInt(addressPrefix.toString());
}
if (!addressPrefix) {
addressPrefix = cnUtil.address_decode_integrated(addressBuffer);
}
return addressPrefix || null;
}
exports.getAddressPrefix = getAddressPrefix;
// Validate miner address
exports.validateMinerAddress = function (address) {
// First check if this is a Salvium pool and use Salvium validation
if (isSalviumEnabled()) {
return validateSalviumAddress(address, 'cryptonote') ||
validateSalviumAddress(address, 'carrot');
}
// Default validation for other coins
let addressPrefix = getAddressPrefix(address);
if (addressPrefix === addressBase58Prefix) return true;
else if (addressPrefix === integratedAddressBase58Prefix) return true;
else if (addressPrefix === subAddressBase58Prefix) return true;
return false;
}
function characterCount (string, char) {
let re = new RegExp(char, "gi")
let matches = string.match(re)
return matches === null ? 0 : matches.length;
}
2021-01-04 12:56:28 -05:00
exports.characterCount = characterCount;
// Validate miner address
exports.validateChildMinerAddress = (address, index) => {
2021-01-19 22:49:31 -05:00
let childAddressBase58Prefix = parseInt(cnUtil.address_decode(Buffer.from(config.childPools[index].poolAddress)).toString());
let childIntegratedAddressBase58Prefix = config.poolServer.intChildAddressPrefix ? parseInt(config.childPools[index].intAddressPrefix) : childAddressBase58Prefix + 1;
let addressPrefix = getAddressPrefix(address);
if (addressPrefix === childAddressBase58Prefix) return true;
else if (addressPrefix === childIntegratedAddressBase58Prefix) return true;
return false;
}
// Return if value is an integrated address
exports.isIntegratedAddress = function (address) {
let addressPrefix = getAddressPrefix(address);
return (addressPrefix === integratedAddressBase58Prefix);
}
exports.determineRewardData = (value) => {
let calculatedData = {
'address': value,
'rewardType': 'prop'
}
if (/^solo:/i.test(value)) {
calculatedData['address'] = value.substr(5)
calculatedData['rewardType'] = 'solo'
return calculatedData
}
if (/^prop:/i.test(value)) {
calculatedData['address'] = value.substr(5)
calculatedData['rewardType'] = 'prop'
return calculatedData
}
return calculatedData
}
/**
* Cleanup special characters (fix for non latin characters)
**/
function cleanupSpecialChars (str) {
str = str.replace(/[ÀÁÂÃÄÅ]/g, "A");
str = str.replace(/[àáâãäå]/g, "a");
str = str.replace(/[ÈÉÊË]/g, "E");
str = str.replace(/[èéêë]/g, "e");
str = str.replace(/[ÌÎÏ]/g, "I");
str = str.replace(/[ìîï]/g, "i");
str = str.replace(/[ÒÔÖ]/g, "O");
str = str.replace(/[òôö]/g, "o");
str = str.replace(/[ÙÛÜ]/g, "U");
str = str.replace(/[ùûü]/g, "u");
return str.replace(/[^A-Za-z0-9\-\_+]/gi, '');
}
exports.cleanupSpecialChars = cleanupSpecialChars;
/**
* Get readable hashrate
**/
exports.getReadableHashRate = function (hashrate) {
let i = 0;
let byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH'];
while (hashrate > 1000) {
hashrate = hashrate / 1000;
i++;
}
return hashrate.toFixed(2) + byteUnits[i] + '/sec';
}
/**
* Get readable coins
**/
exports.getReadableCoins = function (coins, digits, withoutSymbol) {
2021-01-19 22:49:31 -05:00
let coinDecimalPlaces = config.coinDecimalPlaces || config.coinUnits.toString().length - 1;
let amount = (parseInt(coins || 0) / config.coinUnits).toFixed(digits || coinDecimalPlaces);
return amount + (withoutSymbol ? '' : (' ' + config.symbol));
}
/**
* Generate unique id
**/
exports.uid = function () {
let min = 100000000000000;
let max = 999999999999999;
let id = Math.floor(Math.random() * (max - min + 1)) + min;
return id.toString();
};
/**
* Ring buffer
**/
exports.ringBuffer = function (maxSize) {
let data = [];
let cursor = 0;
let isFull = false;
return {
append: function (x) {
if (isFull) {
data[cursor] = x;
cursor = (cursor + 1) % maxSize;
} else {
data.push(x);
cursor++;
if (data.length === maxSize) {
cursor = 0;
isFull = true;
}
}
},
avg: function (plusOne) {
let sum = data.reduce(function (a, b) {
return a + b
}, plusOne || 0);
return sum / ((isFull ? maxSize : cursor) + (plusOne ? 1 : 0));
},
size: function () {
return isFull ? maxSize : cursor;
},
clear: function () {
data = [];
cursor = 0;
isFull = false;
}
};
};
/**
* Salvium-specific utility functions
**/
// Check if Salvium functionality is enabled
function isSalviumEnabled() {
return config.salvium && config.salvium.enabled === true &&
(config.coin === 'Salvium' || config.coin === 'SAL' || config.symbol === 'SAL' || config.symbol === 'SAL1');
}
exports.isSalviumEnabled = isSalviumEnabled;
// Get current Salvium state based on block height
function getSalviumState(height) {
if (!isSalviumEnabled() || !height) return 'disabled';
if (height >= config.salvium.heights.carrot) return 'carrot_payouts';
if (height >= config.salvium.heights.require_dual_login) return 'dual_required';
if (height >= config.salvium.heights.audit_complete) return 'payout_resume';
if (height >= config.salvium.heights.audit_phase1) return 'payout_blackout';
return 'normal';
}
exports.getSalviumState = getSalviumState;
// Validate Salvium address with specific prefixes
function validateSalviumAddress(address, type) {
if (!isSalviumEnabled() || !address) return false;
const sep =
(config.salvium && config.salvium.addressSeparator) ||
(config.poolServer && config.poolServer.paymentId && config.poolServer.paymentId.addressSeparator) ||
"_";
// default to cryptonote, case-insensitive
const normType = (type || "cryptonote").toLowerCase();
// choose which part of a dual address to validate
let candidate = address;
if (address.includes(sep)) {
const parts = address.split(sep);
candidate = normType === "carrot" ? (parts[1] || "") : (parts[0] || "");
}
// get prefix from the selected candidate
const addressPrefix = getAddressPrefix(candidate);
if (!addressPrefix && addressPrefix !== 0) return false;
// pick prefixes set for the requested type
const prefixes =
config.salvium &&
config.salvium.addressPrefixes &&
config.salvium.addressPrefixes[normType];
if (!prefixes) return false;
// support both hex strings ("0x...") and decimal numbers in config
const toInt = (v) => {
if (typeof v === "number") return v;
if (typeof v === "string") {
const s = v.trim().toLowerCase();
if (s.startsWith("0x")) return parseInt(s, 16);
const n = parseInt(s, 10);
return Number.isFinite(n) ? n : NaN;
}
return NaN;
};
const pub = toInt(prefixes.public);
const integ = toInt(prefixes.integrated);
const sub = toInt(prefixes.subaddress);
if (![pub, integ, sub].every(Number.isFinite)) return false;
// Optional: keep your existing debug log
log("warn", logSystem, "prefixy %s : %s : %s", [candidate, addressPrefix, pub]);
return (
addressPrefix === pub ||
addressPrefix === integ ||
addressPrefix === sub
);
}
exports.validateSalviumAddress = validateSalviumAddress;
// Parse dual address for Salvium
function parseSalviumDualAddress(login) {
if (!isSalviumEnabled()) return null;
let separator = config.salvium.addressSeparator || config.poolServer.paymentId.addressSeparator;
let parts = login.split(separator);
if (parts.length < 2) return null;
let cryptonoteAddress = parts[0];
let carrotAddress = parts[1];
// Validate both addresses
if (!validateSalviumAddress(cryptonoteAddress, 'cryptonote') ||
!validateSalviumAddress(carrotAddress, 'carrot')) {
return null;
}
return {
cryptonote: cryptonoteAddress,
carrot: carrotAddress,
original: login
};
}
exports.parseSalviumDualAddress = parseSalviumDualAddress;
// Get appropriate pool address based on current height
function getSalviumPoolAddress(height) {
if (!isSalviumEnabled()) return config.poolServer.poolAddress;
let state = getSalviumState(height);
if (state === 'carrot_payouts' && config.salvium.carrotPoolAddress) {
return config.salvium.carrotPoolAddress;
}
return config.poolServer.poolAddress;
}
exports.getSalviumPoolAddress = getSalviumPoolAddress;