From 8d538ac80e49b5fa2be88a738e48e9c8cb7b02d7 Mon Sep 17 00:00:00 2001 From: argonmining Date: Thu, 22 Aug 2024 12:52:21 -0500 Subject: [PATCH] Hashrate Calc improvements: We now use the actual time span of the shares for the window size, capped at 10 minutes. This adapts better to varying share submission rates. The weighting system now uses an exponential decay function, which gives more emphasis to recent shares while still considering older ones. Error handling has been added to prevent a single miner's calculation from breaking the entire function. We now store the calculated hashrate in the workerStats object for potential use elsewhere in the application. The total hashrate is now reported with fixed decimal places for consistency. These changes should provide a more accurate and robust hashrate calculation while maintaining good performance. The exponential decay weighting will make the hashrate more responsive to recent changes in a miner's performance. --- src/stratum/sharesManager.ts | 75 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/src/stratum/sharesManager.ts b/src/stratum/sharesManager.ts index 5144833..8bf84c1 100644 --- a/src/stratum/sharesManager.ts +++ b/src/stratum/sharesManager.ts @@ -33,6 +33,7 @@ export interface WorkerStats { varDiffWindow: number; minDiff: number; recentShares: Denque<{ timestamp: number, difficulty: number }>; + hashrate: number; // Added hashrate property } type MinerData = { @@ -82,7 +83,8 @@ export class SharesManager { varDiffSharesFound: 0, varDiffWindow: 0, minDiff: 1, // Set to initial difficulty - recentShares: new Denque<{ timestamp: number, difficulty: number }>() // Initialize denque correctly + recentShares: new Denque<{ timestamp: number, difficulty: number }>(), // Initialize denque correctly + hashrate: 0 // Initialize hashrate property }; minerData.workerStats = workerStats; if (DEBUG) this.monitoring.debug(`SharesManager: Created new worker stats for ${workerName}`); @@ -130,7 +132,8 @@ export class SharesManager { varDiffSharesFound: 0, varDiffWindow: 0, minDiff: currentDifficulty, - recentShares: new Denque<{ timestamp: number, difficulty: number }>() // Initialize recentShares + recentShares: new Denque<{ timestamp: number, difficulty: number }>(), // Initialize recentShares + hashrate: 0 // Initialize hashrate property } }; this.miners.set(address, minerData); @@ -255,39 +258,52 @@ export class SharesManager { calcHashRates() { let totalHashRate = 0; const baseWindowSize = 10 * 60 * 1000; // 10 minutes base window + const now = Date.now(); this.miners.forEach((minerData, address) => { - const now = Date.now(); - - // Adjust the window size dynamically based on miner's activity - const sharesCount = minerData.workerStats.recentShares.length; - const windowSize = Math.min(baseWindowSize, sharesCount * 1000); // Minimum 1 second per share, max 10 min - - // Extract relevant shares from recentShares - const relevantShares = minerData.workerStats.recentShares.toArray().filter(share => now - share.timestamp <= windowSize); - - if (relevantShares.length === 0) return; - - // Weighted average based on time proximity - let totalDifficulty = 0; - let totalWeight = 0; - relevantShares.forEach((share, index) => { - const weight = 1 + (index / relevantShares.length); // More recent shares get more weight - totalDifficulty += share.difficulty * weight; - totalWeight += weight; - }); - - const avgDifficulty = totalDifficulty / totalWeight; - const timeDifference = (now - relevantShares[0].timestamp) / 1000; // in seconds - - const workerHashRate = (avgDifficulty * relevantShares.length) / timeDifference; - metrics.updateGaugeValue(minerHashRateGauge, [minerData.workerStats.workerName, address], workerHashRate); - totalHashRate += workerHashRate; + try { + const workerStats = minerData.workerStats; + const recentShares = workerStats.recentShares.toArray(); + + if (recentShares.length === 0) return; + + // Adjust the window size dynamically based on miner's activity + const oldestShareTime = recentShares[0].timestamp; + const windowSize = Math.min(baseWindowSize, now - oldestShareTime); + + // Filter relevant shares + const relevantShares = recentShares.filter(share => now - share.timestamp <= windowSize); + + if (relevantShares.length === 0) return; + + // Calculate weighted average difficulty + let totalWeightedDifficulty = 0; + let totalWeight = 0; + relevantShares.forEach((share, index) => { + const age = (now - share.timestamp) / windowSize; + const weight = Math.exp(-5 * age); // Exponential decay + totalWeightedDifficulty += share.difficulty * weight; + totalWeight += weight; + }); + + const avgDifficulty = totalWeightedDifficulty / totalWeight; + const timeDifference = (now - relevantShares[relevantShares.length - 1].timestamp) / 1000; // in seconds + + const workerHashRate = (avgDifficulty * relevantShares.length) / timeDifference; + + metrics.updateGaugeValue(minerHashRateGauge, [workerStats.workerName, address], workerHashRate); + totalHashRate += workerHashRate; + + // Update worker's hashrate in workerStats + workerStats.hashrate = workerHashRate; + } catch (error) { + this.monitoring.error(`Error calculating hashrate for miner ${address}: ${error}`); + } }); metrics.updateGaugeValue(poolHashRateGauge, ['pool', this.poolAddress], totalHashRate); if (DEBUG) { - this.monitoring.debug(`SharesManager: Total pool hash rate updated to ${totalHashRate} GH/s`); + this.monitoring.debug(`SharesManager: Total pool hash rate updated to ${totalHashRate.toFixed(6)} GH/s`); } } @@ -352,7 +368,6 @@ export class SharesManager { this.monitoring.debug(`SharesManager: VarDiff - Adjusting difficulty for ${stats.workerName} from ${stats.minDiff} to ${newDiff}`); stats.minDiff = newDiff; this.updateSocketDifficulty(address, newDiff); - varDiff.labels(stats.workerName).set(newDiff); } else { this.monitoring.debug(`SharesManager: VarDiff - No change in difficulty for ${stats.workerName} (current difficulty: ${stats.minDiff})`); }