From 999186ab6d870a88e183a2f9c0919b57cd292bf4 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sat, 21 Mar 2026 15:08:23 +0100 Subject: [PATCH] Adapt solo pool for current Peya and shared util --- config_examples/peya.json | 139 +++++++++++++++++++++++++ lib/daemon.js | 20 ++-- lib/notifications.js | 31 ++++-- lib/paymentProcessor.js | 189 ++++----------------------------- lib/pool.js | 72 ++++++------- lib/utils.js | 213 ++++++++++++++------------------------ package.json | 4 +- 7 files changed, 305 insertions(+), 363 deletions(-) create mode 100644 config_examples/peya.json diff --git a/config_examples/peya.json b/config_examples/peya.json new file mode 100644 index 0000000..5a6786f --- /dev/null +++ b/config_examples/peya.json @@ -0,0 +1,139 @@ +{ + "poolHost": "your.peya.pool.host", + + "coin": "Peya", + "symbol": "PEY", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 12, + "coinDifficultyTarget": 120, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 15, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "peya": { + "enabled": true, + "assetSymbol": "PEY", + "poolAddress": "PtYa_YOUR_POOL_CARROT_ADDRESS_HERE", + "addressPrefixes": { + "carrot": { + "public": "0x2ceb88", + "integrated": "0x30eb88", + "subaddress": "0x356b88" + } + } + }, + + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "PtYa_YOUR_POOL_CARROT_ADDRESS_HERE", + "intAddressPrefix": null, + "blockRefreshInterval": 600, + "minerTimeout": 600, + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + } + ], + "varDiff": { + "minDiff": 5000, + "maxDiff": 100000000, + "targetTime": 120, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 80, + "checkThreshold": 30 + } + }, + + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 50, + "mixin": 16, + "priority": 0, + "transferFee": 1000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxTransactionAmount": 100000000000000, + "denomination": 1000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 1.0, + "devDonation": 0.1, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8070, + "blocks": 30, + "payments": 30, + "password": "your_api_password", + "ssl": false, + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 37777 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 37778 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + } +} diff --git a/lib/daemon.js b/lib/daemon.js index 7a875ab..871e976 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -17,17 +17,6 @@ let blockData = JSON.stringify({ params: {} }) -let templateData = JSON.stringify({ - id: "0", - jsonrpc: "2.0", - method: 'getblocktemplate', - params: { - reserve_size: config.poolServer.mergedMining ? POOL_NONCE_MM_SIZE : POOL_NONCE_SIZE, - wallet_address: config.poolServer.poolAddress - } -}) - - require('./exceptionWriter.js')(logSystem); @@ -62,6 +51,15 @@ function runInterval () { }); }, function (getbc, callback) { + let templateData = JSON.stringify({ + id: "0", + jsonrpc: "2.0", + method: 'getblocktemplate', + params: { + reserve_size: config.poolServer.mergedMining ? POOL_NONCE_MM_SIZE : POOL_NONCE_SIZE, + wallet_address: utils.getPoolTemplateAddress() + } + }); apiInterfaces.jsonHttpRequest(config.daemon.host, config.daemon.port, templateData, function (err, res) { if (err) { log('error', logSystem, 'Error polling getblocktemplate %j', [err]) diff --git a/lib/notifications.js b/lib/notifications.js index 69036a2..687902c 100644 --- a/lib/notifications.js +++ b/lib/notifications.js @@ -11,10 +11,25 @@ // Load required modules let fs = require('fs'); -let emailSystem = require('./email.js'); -let telegram = require('./telegram.js'); let utils = require('./utils.js'); +let emailSystem = null; +let telegram = null; + +function getEmailSystem () { + if (!emailSystem) { + emailSystem = require('./email.js'); + } + return emailSystem; +} + +function getTelegram () { + if (!telegram) { + telegram = require('./telegram.js'); + } + return telegram; +} + // Initialize log system let logSystem = 'notifications'; require('./exceptionWriter.js')(logSystem); @@ -111,7 +126,7 @@ function sendToTelegramChannel (id, variables) { } let chatId = '@' + channel; - telegram.sendMessage(chatId, message); + getTelegram().sendMessage(chatId, message); } } exports.sendToTelegramChannel = sendToTelegramChannel; @@ -133,7 +148,7 @@ function sendToMinerTelegram (miner, id, variables) { redisClient.hget(config.coin + ':telegram', miner, function (error, chatId) { if (error || !chatId) return; - telegram.sendMessage(chatId, message); + getTelegram().sendMessage(chatId, message); }); } } @@ -158,7 +173,7 @@ function sendBlockTelegram (id, variables) { if (error || !data) return; for (let chatId in data) { if (!chatId) continue; - telegram.sendMessage(chatId, message); + getTelegram().sendMessage(chatId, message); } }); } @@ -185,7 +200,7 @@ function sendToAllEmails (id, variables) { if (error || !data) return; for (let address in data) { let email = data[address]; - emailSystem.sendEmail(email, subject, content); + getEmailSystem().sendEmail(email, subject, content); } }); } @@ -210,7 +225,7 @@ function sendToMinerEmail (miner, id, variables) { redisClient.hget(config.coin + ':notifications', miner, function (error, email) { if (error || !email) return; - emailSystem.sendEmail(email, subject, content); + getEmailSystem().sendEmail(email, subject, content); }); } } @@ -232,7 +247,7 @@ function sendToEmail (email, id, variables) { return; } - emailSystem.sendEmail(email, subject, content); + getEmailSystem().sendEmail(email, subject, content); } } exports.sendToEmail = sendToEmail; diff --git a/lib/paymentProcessor.js b/lib/paymentProcessor.js index a9636cb..5d0eee0 100644 --- a/lib/paymentProcessor.js +++ b/lib/paymentProcessor.js @@ -27,74 +27,23 @@ if (!config.poolServer.paymentId) config.poolServer.paymentId = {}; if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.addressSeparator = "+"; if (!config.payments.priority) config.payments.priority = 0; -// how often to attempt payouts -const PAY_INTERVAL_MS = - ((config.payments && config.payments.interval) ? config.payments.interval : 60) * 1000; +function applyAssetAwareTransferFields (transferCmd) { + if (!utils.isAssetAwareCoin() || !transferCmd || !transferCmd.rpc) return; -let payoutTimer = null; -let isRoundActive = false; + let assetSymbol = utils.getPrimaryAssetSymbol(); -function scheduleNext(ms = PAY_INTERVAL_MS) { - if (payoutTimer) clearTimeout(payoutTimer); - payoutTimer = setTimeout(runInterval, ms); + if (transferCmd.rpc.destinations && Array.isArray(transferCmd.rpc.destinations)) { + transferCmd.rpc.destinations.forEach(destination => { + destination.asset_type = assetSymbol; + }); + } + + transferCmd.rpc.source_asset = assetSymbol; + transferCmd.rpc.dest_asset = assetSymbol; + transferCmd.rpc.tx_type = 3; } function runInterval () { - if (isRoundActive) { - // avoid overlapping rounds - log('warn', logSystem, 'Previous payment round still running — skipping this tick'); - return scheduleNext(); - } - isRoundActive = true; - - const done = (delayMs = PAY_INTERVAL_MS) => { - isRoundActive = false; - scheduleNext(delayMs); - }; - - // Non-Salvium path - if (!utils.isSalviumEnabled()) { - try { - processSalviumAwarePayments(0); - } catch (e) { - log('error', logSystem, 'Non-Salvium payment round crashed: %j', [e && e.stack || e]); - // small backoff on error - return done(Math.min(15000, PAY_INTERVAL_MS)); - } - return done(); - } - - // Salvium path - apiInterfaces.rpcDaemon('getblockcount', [], function (error, result) { - if (error) { - log('error', logSystem, 'Error getting block count for Salvium check: %j', [error]); - // IMPORTANT: always schedule the next tick, even on error - return done(Math.min(15000, PAY_INTERVAL_MS)); - } - - log('info', logSystem, 'payout height: %s', [result.count]); - const currentHeight = (result && typeof result.count === 'number') ? (result.count - 1) : 0; - - try { - const salviumState = utils.getSalviumState(currentHeight); - - if (salviumState === 'payout_blackout') { - log('info', logSystem, 'Salvium payout blackout at height %d — skipping.', [currentHeight]); - return done(); // schedule next normal tick - } - - // proceed with payments for this height - processSalviumAwarePayments(currentHeight); - } catch (e) { - log('error', logSystem, 'Salvium payment round crashed at height %d: %j', [currentHeight, e && e.stack || e]); - return done(Math.min(15000, PAY_INTERVAL_MS)); - } - - return done(); - }); -} - -function processSalviumAwarePayments(currentHeight) { async.waterfall([ // Get worker keys @@ -208,88 +157,15 @@ function processSalviumAwarePayments(currentHeight) { let address = worker; let payment_id = null; + let with_payment_id = false; - // Handle Salvium dual address and carrot switching - if (utils.isSalviumEnabled()) { - const salviumState = utils.getSalviumState(currentHeight); - - const dualSep = - (config.salvium && config.salvium.addressSeparator) || - (config.poolServer && config.poolServer.paymentId && config.poolServer.paymentId.addressSeparator) || - '_'; - - const pidSep = (config.poolServer && config.poolServer.paymentId && config.poolServer.paymentId.addressSeparator) || '.'; - - const login = worker; // what miners provided / what you stored per worker - const hasDual = typeof login === 'string' && login.includes(dualSep); - - // Helpers to pick candidates - const [cnCandidateRaw, carrotCandidateRaw] = hasDual ? login.split(dualSep) : [login, login]; - - // Helper: peel payment_id ONLY for cryptonote - const peelPid = (addr) => { - const [a, pid] = String(addr).split(pidSep); - return { addr: a, pid: pid || null }; - }; - - if (salviumState === 'carrot_payouts') { - // Must pay to CARROT. Accept dual (use right part) OR single carrot. - const carrotPick = hasDual ? carrotCandidateRaw : login; - - if (!utils.validateSalviumAddress(carrotPick, 'carrot')) { - log('warn', logSystem, - 'Salvium carrot_payouts: invalid carrot address for worker "%s": %s', - [worker, carrotPick]); - // handle per your flow: skip/throw - // e.g., throw new Error('Invalid carrot payout address'); - } else { - address = carrotPick; - payment_id = null; // carrot payouts never use payment_id - log('info', logSystem, 'Salvium: Paying CARROT address %s', [address]); - } - - } else { - // Pay to CRYPTONOTE. Two cases: - // - dual_required OR login contains separator: expect dual OR single CN (no single carrot) - // - otherwise: must be single CN - const mustAllowDual = salviumState === 'dual_required' || hasDual; - - const cnPickRaw = hasDual ? cnCandidateRaw : login; - const { addr: cnPick, pid } = peelPid(cnPickRaw); - - if (!utils.validateSalviumAddress(cnPick, 'cryptonote')) { - log('warn', logSystem, - 'Salvium CN payout: invalid cryptonote address for worker "%s": %s', - [worker, cnPick]); - continue; - // handle per your flow: skip/throw - // e.g., throw new Error('Invalid cryptonote payout address'); - } else { - address = cnPick; - payment_id = pid; - if (salviumState === 'dual_required' && !hasDual) { - // Allowed by spec ("...or single cryptonote"), but note it - log('warn', logSystem, - 'Salvium dual_required: single cryptonote login accepted for %s', - [worker]); - } - log('info', logSystem, 'Salvium: Paying CRYPTONOTE address %s%s', - [address, payment_id ? ` (pid ${payment_id})` : '']); - } - } - } - else { - // Standard address parsing for non-Salvium coins - let addr = address.split(config.poolServer.paymentId.addressSeparator); - address = addr[0]; - payment_id = addr[1] || null; - } - - // Payment ID validation (existing logic) - if ((utils.isIntegratedAddress && utils.isIntegratedAddress(address)) || payment_id) { + let addr = address.split(config.poolServer.paymentId.addressSeparator); + if ((addr.length === 1 && utils.isIntegratedAddress(address)) || addr.length >= 2) { with_payment_id = true; - if (payment_id) { + if (addr.length >= 2) { + address = addr[0]; + payment_id = addr[1]; payment_id = payment_id.replace(/[^A-Za-z0-9]/g, ''); if (payment_id.length !== 16 && payment_id.length !== 64) { with_payment_id = false; @@ -304,7 +180,7 @@ function processSalviumAwarePayments(currentHeight) { } if (config.poolServer.fixedDiff && config.poolServer.fixedDiff.enabled) { - let addr = address.split(config.poolServer.fixedDiff.addressSeparator); + addr = address.split(config.poolServer.fixedDiff.addressSeparator); if (addr.length >= 2) address = addr[0]; } @@ -360,32 +236,7 @@ function processSalviumAwarePayments(currentHeight) { async.filter(transferCommands, function (transferCmd, cback) { let rpcCommand = "transfer"; let rpcRequest = transferCmd.rpc; - - // Handle Salvium asset type settings based on height - if (utils.isSalviumEnabled()) { - // Guard against missing structures - if (transferCmd && transferCmd.rpc) { - // Compute effective symbol by height - let transitionHeight = config.salvium && config.salvium.heights && config.salvium.heights.audit_phase1 - ? config.salvium.heights.audit_phase1 - : 815; - let effectiveSymbol = (currentHeight >= transitionHeight) ? 'SAL1' : 'SAL'; - - // Set asset fields for all destinations - if (transferCmd.rpc.destinations && Array.isArray(transferCmd.rpc.destinations)) { - transferCmd.rpc.destinations.forEach(destination => { - destination.asset_type = effectiveSymbol; - }); - } - - // Set source and destination assets - transferCmd.rpc.source_asset = effectiveSymbol; - transferCmd.rpc.dest_asset = effectiveSymbol; - - // Always set tx_type = 3 for Salvium (both phases) - transferCmd.rpc.tx_type = 3; - } - } + applyAssetAwareTransferFields(transferCmd); if (daemonType === "bytecoin") { diff --git a/lib/pool.js b/lib/pool.js index 402b979..592307d 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -10,7 +10,6 @@ let fs = require('fs'); let net = require('net'); let tls = require('tls'); let async = require('async'); -let bignum = require('bignum'); let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); let notifications = require('./notifications.js'); @@ -128,7 +127,26 @@ let currentChildBlockTemplate = new Array(mergedMining ? config.childPools.lengt // Difficulty buffer -let diff1 = bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16); +const DIFF1 = (1n << 256n) - 1n; + +function bigintToBuffer (value) { + if (value === 0n) return Buffer.from([0]); + let hex = value.toString(16); + if (hex.length % 2 !== 0) hex = '0' + hex; + return Buffer.from(hex, 'hex'); +} + +function bufferToBigInt (buffer) { + let hex = Buffer.from(buffer).toString('hex'); + if (!hex) return 0n; + return BigInt('0x' + hex); +} + +function calculateDifficultyBigInt (hashBuffer) { + let hashNum = bufferToBigInt(hashBuffer); + if (hashNum === 0n) return DIFF1; + return DIFF1 / hashNum; +} /** * Convert buffer to byte array @@ -458,7 +476,7 @@ Miner.prototype = { } let padded = Buffer.alloc(32); padded.fill(0); - let diffBuff = diff1.div(this.difficulty).toBuffer(); + let diffBuff = bigintToBuffer(DIFF1 / BigInt(this.difficulty)); diffBuff.copy(padded, 32 - diffBuff.length); let buff = padded.slice(0, 4); let buffArray = buff.toByteArray().reverse(); @@ -724,35 +742,9 @@ function handleMinerMethod (method, params, ip, portData, sendReply, pushMessage } } - // Handle Salvium dual address login validation - if (utils.isSalviumEnabled()) { - let currentHeight = currentBlockTemplate[0] ? currentBlockTemplate[0].height : 0; - let salviumState = utils.getSalviumState(currentHeight); - - if (salviumState === 'dual_required' || salviumState === 'carrot_payouts') { - // Dual address required for these states - let dualAddress = utils.parseSalviumDualAddress(login); - if (!dualAddress) { - log('warn', logSystem, 'Dual address required for Salvium at height %d: %s', [currentHeight, login]); - sendReply('Dual address required. Format: [cryptonote_address][separator][carrot_address]'); - return; - } - address = dualAddress.cryptonote; // Use cryptonote address for mining - // Store dual address info for payment processing - login = dualAddress.original; - } else { - // Normal address parsing for earlier heights - addr = login.split(config.poolServer.paymentId.addressSeparator); - address = addr[0] || null; - paymentId = addr[1] || null; - } - } else { - // Standard address parsing for non-Salvium coins - addr = login.split(config.poolServer.paymentId.addressSeparator); - address = addr[0] || null; - paymentId = addr[1] || null; - } - + addr = login.split(config.poolServer.paymentId.addressSeparator); + address = addr[0] || null; + paymentId = addr[1] || null; if (!address) { log('warn', logSystem, 'No address specified for login'); sendReply('Invalid address used for login'); @@ -1223,12 +1215,12 @@ function processShare (miner, job, blockTemplate, params) { return false; } - let hashArray = hash.toByteArray() - .reverse(); - let hashNum = bignum.fromBuffer(Buffer.from(hashArray)); - let hashDiff = diff1.div(hashNum); + let hashArray = hash.toByteArray().reverse(); + let hashDiff = calculateDifficultyBigInt(Buffer.from(hashArray)); + let blockDifficulty = BigInt(blockTemplate.difficulty); + let jobDifficulty = BigInt(job.difficulty); - if (hashDiff.ge(blockTemplate.difficulty)) { + if (hashDiff >= blockDifficulty) { apiInterfaces.rpcDaemon('submitblock', [shareBuffer.toString('hex')], function (error, result) { if (error) { @@ -1242,7 +1234,7 @@ function processShare (miner, job, blockTemplate, params) { recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate, null); } }); - } else if (hashDiff.lt(job.difficulty)) { + } else if (hashDiff < jobDifficulty) { log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); return false; } else { @@ -1258,7 +1250,7 @@ function processShare (miner, job, blockTemplate, params) { if (childBlockTemplate) { if (mergedMining) { let pool = config.childPools[miner.activeChildPool] - if (hashDiff.ge(childBlockTemplate.difficulty)) { + if (hashDiff >= BigInt(childBlockTemplate.difficulty)) { let mergedBuffer = null try { mergedBuffer = utils.cnUtil.construct_mm_child_block_blob(shareBuffer, cnBlobType, childBlockTemplate.buffer); @@ -1285,7 +1277,7 @@ function processShare (miner, job, blockTemplate, params) { } }, pool.childDaemon); } - } else if (hashDiff.lt(job.difficulty)) { + } else if (hashDiff < jobDifficulty) { log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.workerName, miner.ip]); return false; } else { diff --git a/lib/utils.js b/lib/utils.js index 4199dd8..dc768fa 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,17 +14,11 @@ exports.dateFormat = dateFormat; let cnUtil = require('cryptoforknote-util'); exports.cnUtil = cnUtil; - require('./configReader.js'); +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 **/ @@ -57,13 +51,14 @@ 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'); + if (isPeyaEnabled()) { + return validateConfiguredAddress(address, 'peya', 'carrot'); } - - // Default validation for other coins + + if (isSalviumEnabled()) { + return validateConfiguredAddress(address, 'salvium', 'carrot'); + } + let addressPrefix = getAddressPrefix(address); if (addressPrefix === addressBase58Prefix) return true; else if (addressPrefix === integratedAddressBase58Prefix) return true; @@ -131,6 +126,78 @@ function cleanupSpecialChars (str) { } exports.cleanupSpecialChars = cleanupSpecialChars; +function isPeyaEnabled() { + return config.peya && config.peya.enabled === true && + (config.coin === 'Peya' || config.coin === 'PEY' || config.symbol === 'PEY'); +} +exports.isPeyaEnabled = isPeyaEnabled; + +function isSalviumEnabled() { + return config.salvium && config.salvium.enabled === true && + (config.coin === 'Salvium' || config.coin === 'SAL' || config.symbol === 'SAL' || config.symbol === 'SAL1'); +} +exports.isSalviumEnabled = isSalviumEnabled; + +function isAssetAwareCoin() { + return isSalviumEnabled() || isPeyaEnabled(); +} +exports.isAssetAwareCoin = isAssetAwareCoin; + +function getConfiguredPrefixes(sectionName, type) { + return config[sectionName] && + config[sectionName].addressPrefixes && + config[sectionName].addressPrefixes[type]; +} + +function validateConfiguredAddress(address, sectionName, type) { + if (!address) return false; + + const prefixes = getConfiguredPrefixes(sectionName, type); + if (!prefixes) return false; + + const addressPrefix = getAddressPrefix(address); + if (addressPrefix == null) return false; + + const toInt = (v) => { + if (typeof v === 'number') return v; + if (typeof v === 'string') return parseInt(v); + return NaN; + }; + + return [prefixes.public, prefixes.integrated, prefixes.subaddress] + .map(toInt) + .some(prefix => !Number.isNaN(prefix) && prefix === addressPrefix); +} +exports.validateConfiguredAddress = validateConfiguredAddress; + +function getPrimaryAssetSymbol() { + if (isPeyaEnabled()) { + return (config.peya && config.peya.assetSymbol) || 'PEY'; + } + + if (isSalviumEnabled()) { + return (config.salvium && config.salvium.assetSymbol) || 'SAL1'; + } + + return config.symbol; +} +exports.getPrimaryAssetSymbol = getPrimaryAssetSymbol; + +function getPoolTemplateAddress() { + if (isPeyaEnabled()) { + return (config.peya && config.peya.poolAddress) || config.poolServer.poolAddress; + } + + if (isSalviumEnabled()) { + return (config.salvium && config.salvium.poolAddress) || + (config.salvium && config.salvium.carrotPoolAddress) || + config.poolServer.poolAddress; + } + + return config.poolServer.poolAddress; +} +exports.getPoolTemplateAddress = getPoolTemplateAddress; + /** * Get readable hashrate **/ @@ -201,123 +268,3 @@ exports.ringBuffer = function (maxSize) { } }; }; - -/** - * 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; diff --git a/package.json b/package.json index 2b83a22..e78f14a 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "bignum": "*", "bufferutil": "*", "cli-color": "*", - "cryptoforknote-util": "git+https://github.com/MoneroOcean/node-cryptoforknote-util.git", - "cryptonight-hashing": "git+https://github.com/MoneroOcean/node-cryptonight-hashing.git", + "cryptoforknote-util": "file:../node-cryptoforknote-util-salvium", + "cryptonight-hashing": "file:../node-cryptonight-hashing-mo", "dateformat": "^4.5.1", "domexception": "*", "mailgun.js": "*",