@@ -63,7 +63,7 @@ def _env_or_default(name: str, default: str) -> str:
6363SUPPORT_TICKET_URL = _env_or_default ("SUPPORT_TICKET_URL" , f"{ DEFAULT_SUPPORT_BASE_URL } /api/support/upload" )
6464
6565APP_ID = "willitmod-dev-dgb"
66- APP_VERSION = "0.8.20 "
66+ APP_VERSION = "0.8.21 "
6767
6868DGB_RPC_HOST = os .getenv ("DGB_RPC_HOST" , "dgbd" )
6969DGB_RPC_PORT = int (os .getenv ("DGB_RPC_PORT" , "14022" ))
@@ -205,11 +205,16 @@ def _pool_workers_from_db(pool_id: str):
205205 )
206206 rows = cur .fetchall () or []
207207
208- # Pull "last share" timestamps from shares (minerstats.created is a snapshot timestamp, not the last share time).
209- # Limit the scan window to keep it cheap even on busy pools.
208+ # Pull "last share" timestamps and an estimated hashrate from shares
209+ # (minerstats.created is a snapshot timestamp, not the last share time).
210+ # H/s over 10m ≈ sum(difficulty)*2^32 / 600.
210211 cur .execute (
211212 """
212- SELECT miner, worker, MAX(created) AS last_share
213+ SELECT
214+ miner,
215+ worker,
216+ MAX(created) AS last_share,
217+ COALESCE(SUM(CASE WHEN created >= (NOW() AT TIME ZONE 'utc') - INTERVAL '10 minutes' THEN difficulty ELSE 0 END), 0) AS sumdiff_10m
213218 FROM shares
214219 WHERE poolid = %s AND created >= (NOW() AT TIME ZONE 'utc') - INTERVAL '2 days'
215220 GROUP BY miner, worker
@@ -226,16 +231,22 @@ def _pool_workers_from_db(pool_id: str):
226231 pass
227232
228233 last_share_by_key : dict [tuple [str , str | None ], datetime ] = {}
234+ hashrate_hs_10m_by_key : dict [tuple [str , str | None ], float ] = {}
229235 for r in share_rows :
230236 try :
231- miner , worker , last_share = r
237+ miner , worker , last_share , sumdiff_10m = r
232238 except Exception :
233239 continue
234- if not isinstance (last_share , datetime ):
235- continue
236240 miner_s = str (miner or "" )
237241 worker_s = str (worker or "" ).strip () or None
238- last_share_by_key [(miner_s , worker_s )] = last_share
242+ if isinstance (last_share , datetime ):
243+ last_share_by_key [(miner_s , worker_s )] = last_share
244+ try :
245+ sumdiff_f = float (sumdiff_10m ) if sumdiff_10m is not None else 0.0
246+ if math .isfinite (sumdiff_f ) and sumdiff_f > 0 :
247+ hashrate_hs_10m_by_key [(miner_s , worker_s )] = (sumdiff_f * (2 ** 32 )) / (10 * 60 )
248+ except Exception :
249+ pass
239250
240251 out = []
241252 for r in rows :
@@ -263,8 +274,12 @@ def _pool_workers_from_db(pool_id: str):
263274 except Exception :
264275 pass
265276
266- hashrate_ths = None
267- if hashrate_hs_f is not None :
277+ hashrate_hs_live = hashrate_hs_10m_by_key .get ((miner_s , worker_s ))
278+ hashrate_ths_live = (hashrate_hs_live / 1e12 ) if hashrate_hs_live is not None else None
279+
280+ # Prefer live estimate from accepted shares, fallback to Miningcore minerstats estimate.
281+ hashrate_ths = hashrate_ths_live
282+ if hashrate_ths is None and hashrate_hs_f is not None :
268283 hashrate_ths = hashrate_hs_f / 1e12
269284
270285 last_share = None
@@ -282,6 +297,8 @@ def _pool_workers_from_db(pool_id: str):
282297 "worker" : worker_s ,
283298 "hashrate_hs" : hashrate_hs_f ,
284299 "hashrate_ths" : hashrate_ths ,
300+ "hashrate_hs_live_10m" : hashrate_hs_live ,
301+ "hashrate_ths_live_10m" : hashrate_ths_live ,
285302 "lastShare" : last_share ,
286303 "sharesPerSecond" : shares_per_s ,
287304 }
@@ -1403,6 +1420,19 @@ def _pool_status(pool_id: str, *, algo: str | None = None):
14031420 except Exception :
14041421 share_health = {}
14051422
1423+ hashrate_ths_best_effort = hashrate_ths
1424+ hashrate_ths_live = None
1425+ try :
1426+ hs10m = share_health .get ("hashrate_hs_10m" )
1427+ hs10m_f = float (hs10m ) if hs10m is not None else None
1428+ if hs10m_f is not None and math .isfinite (hs10m_f ) and hs10m_f > 0 :
1429+ hashrate_ths_live = hs10m_f / 1e12
1430+ except Exception :
1431+ hashrate_ths_live = None
1432+
1433+ if hashrate_ths_live is not None :
1434+ hashrate_ths = hashrate_ths_live
1435+
14061436 eta_seconds = None
14071437 try :
14081438 if hashrate_ths and network_difficulty :
@@ -1419,6 +1449,8 @@ def _pool_status(pool_id: str, *, algo: str | None = None):
14191449 "algo" : algo ,
14201450 "workers" : workers_i ,
14211451 "hashrate_ths" : hashrate_ths ,
1452+ "hashrate_ths_best_effort" : hashrate_ths_best_effort ,
1453+ "hashrate_ths_live_10m" : hashrate_ths_live ,
14221454 "total_blocks" : total_blocks ,
14231455 "network_difficulty" : network_difficulty ,
14241456 "network_height" : network_height ,
@@ -1476,6 +1508,8 @@ def _pool_status(pool_id: str, *, algo: str | None = None):
14761508 status .setdefault ("shares_1h" , None )
14771509 status .setdefault ("last_share_at" , None )
14781510 status .setdefault ("eta_seconds" , None )
1511+ status .setdefault ("hashrate_ths_best_effort" , None )
1512+ status .setdefault ("hashrate_ths_live_10m" , None )
14791513 status .setdefault ("hashrates_ths" , {})
14801514 return status
14811515
@@ -1495,6 +1529,8 @@ def _pool_status(pool_id: str, *, algo: str | None = None):
14951529 "shares_1h" : None ,
14961530 "last_share_at" : None ,
14971531 "eta_seconds" : None ,
1532+ "hashrate_ths_best_effort" : None ,
1533+ "hashrate_ths_live_10m" : None ,
14981534 "hashrates_ths" : {},
14991535 "cached" : False ,
15001536 "lastSeen" : int (time .time ()),
@@ -2137,11 +2173,12 @@ def _pool_share_health(pool_id: str) -> dict:
21372173 try :
21382174 cur = conn .cursor ()
21392175 cur .execute (
2140- "SELECT COUNT(*) FROM shares WHERE poolid=%s AND created >= %s" ,
2176+ "SELECT COUNT(*), COALESCE(SUM(difficulty), 0) FROM shares WHERE poolid=%s AND created >= %s" ,
21412177 (pool_id , cutoff_10m ),
21422178 )
21432179 row = cur .fetchone ()
21442180 shares_10m = int (row [0 ]) if row and row [0 ] is not None else 0
2181+ sumdiff_10m = float (row [1 ]) if row and row [1 ] is not None else 0.0
21452182
21462183 cur .execute (
21472184 "SELECT COUNT(*) FROM shares WHERE poolid=%s AND created >= %s" ,
@@ -2165,7 +2202,21 @@ def _pool_share_health(pool_id: str) -> dict:
21652202 return {}
21662203
21672204 last_share_iso = _iso_z (last_share_created ) if isinstance (last_share_created , datetime ) else None
2168- return {"shares_10m" : shares_10m , "shares_1h" : shares_1h , "last_share_at" : last_share_iso }
2205+ # Estimate pool hashrate from accepted shares over the last 10 minutes:
2206+ # H/s ≈ sum(share_difficulty) * 2^32 / window_seconds
2207+ hashrate_hs_10m = None
2208+ try :
2209+ if sumdiff_10m and sumdiff_10m > 0 :
2210+ hashrate_hs_10m = (sumdiff_10m * (2 ** 32 )) / (10 * 60 )
2211+ except Exception :
2212+ hashrate_hs_10m = None
2213+
2214+ return {
2215+ "shares_10m" : shares_10m ,
2216+ "shares_1h" : shares_1h ,
2217+ "last_share_at" : last_share_iso ,
2218+ "hashrate_hs_10m" : hashrate_hs_10m ,
2219+ }
21692220
21702221
21712222def _series_sampler (stop_event : threading .Event ):
0 commit comments