diff --git a/config.json b/config.json index 08a5d10..16058e2 100644 --- a/config.json +++ b/config.json @@ -257,6 +257,33 @@ "currency": "USD" }, + "salvium": { + "enabled": false, + "addressSeparator": "+", + "heights": { + "audit_phase1": 815, + "audit_phase2": 900, + "audit_phase3": 950, + "audit_complete": 1000, + "require_dual_login": 1050, + "carrot": 1100 + }, + "addressPrefixes": { + "cryptonote": { + "public": "0x3ef318", + "integrated": "0x55ef318", + "subaddress": "0xf5ef318" + }, + "carrot": { + "public": "0x180c96", + "integrated": "0x2ccc96", + "subaddress": "0x314c96" + } + }, + "carrotPoolAddress": "", + "carrotDonationAddress": "" + }, + "charts": { "pool": { "hashrate": { diff --git a/lib/paymentProcessor.js b/lib/paymentProcessor.js index 14a86bf..ab89be5 100644 --- a/lib/paymentProcessor.js +++ b/lib/paymentProcessor.js @@ -28,6 +28,33 @@ if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.a if (!config.payments.priority) config.payments.priority = 0; function runInterval () { + // Check for Salvium payout blackout + if (utils.isSalviumEnabled()) { + apiInterfaces.rpcDaemon('getblockcount', [], function (error, result) { + if (error) { + log('error', logSystem, 'Error getting block count for Salvium check: %j', [error]); + return; + } + + let currentHeight = result.count - 1; // getblockcount returns height + 1 + let salviumState = utils.getSalviumState(currentHeight); + + if (salviumState === 'payout_blackout') { + log('info', logSystem, 'Salvium payout blackout active at height %d. Skipping payment processing.', [currentHeight]); + return; + } + + // Continue with normal payment processing + processSalviumAwarePayments(currentHeight); + }); + return; + } + + // Non-Salvium payment processing + processSalviumAwarePayments(0); +} + +function processSalviumAwarePayments(currentHeight) { async.waterfall([ // Get worker keys @@ -141,15 +168,51 @@ function runInterval () { let address = worker; let payment_id = null; - let with_payment_id = false; - let addr = address.split(config.poolServer.paymentId.addressSeparator); - if ((addr.length === 1 && utils.isIntegratedAddress(address)) || addr.length >= 2) { - with_payment_id = true; - if (addr.length >= 2) { + // Handle Salvium dual address and carrot switching + if (utils.isSalviumEnabled()) { + let salviumState = utils.getSalviumState(currentHeight); + + if (salviumState === 'carrot_payouts') { + // Switch to carrot address for payouts + let dualAddress = utils.parseSalviumDualAddress(worker); + if (dualAddress) { + address = dualAddress.carrot; + log('info', logSystem, 'Salvium: Sending payment to carrot address %s', [address]); + } else { + // For backwards compatibility, if no dual address, try to parse as single address + let addr = address.split(config.poolServer.paymentId.addressSeparator); + address = addr[0]; + payment_id = addr[1] || null; + } + } else if (salviumState === 'dual_required') { + // Still pay to cryptonote address but parse dual format + let dualAddress = utils.parseSalviumDualAddress(worker); + if (dualAddress) { + address = dualAddress.cryptonote; + } else { + let addr = address.split(config.poolServer.paymentId.addressSeparator); + address = addr[0]; + payment_id = addr[1] || null; + } + } else { + // Normal payment processing for other heights + let addr = address.split(config.poolServer.paymentId.addressSeparator); address = addr[0]; - payment_id = addr[1]; + payment_id = addr[1] || null; + } + } 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) { + with_payment_id = true; + if (payment_id) { payment_id = payment_id.replace(/[^A-Za-z0-9]/g, ''); if (payment_id.length !== 16 && payment_id.length !== 64) { with_payment_id = false; @@ -164,7 +227,7 @@ function runInterval () { } if (config.poolServer.fixedDiff && config.poolServer.fixedDiff.enabled) { - addr = address.split(config.poolServer.fixedDiff.addressSeparator); + let addr = address.split(config.poolServer.fixedDiff.addressSeparator); if (addr.length >= 2) address = addr[0]; } diff --git a/lib/pool.js b/lib/pool.js index e7ecaa2..402b979 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -724,9 +724,35 @@ function handleMinerMethod (method, params, ip, portData, sendReply, pushMessage } } - addr = login.split(config.poolServer.paymentId.addressSeparator); - address = addr[0] || null; - paymentId = addr[1] || null; + // 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; + } + if (!address) { log('warn', logSystem, 'No address specified for login'); sendReply('Invalid address used for login'); diff --git a/lib/utils.js b/lib/utils.js index de1ef87..6d93af7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -46,6 +46,13 @@ 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; @@ -183,3 +190,81 @@ 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.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; + + let addressPrefix = getAddressPrefix(address); + if (!addressPrefix) return false; + + let prefixes = config.salvium.addressPrefixes[type]; + if (!prefixes) return false; + + return addressPrefix === parseInt(prefixes.public, 16) || + addressPrefix === parseInt(prefixes.integrated, 16) || + addressPrefix === parseInt(prefixes.subaddress, 16); +} +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;