Fixes all 22 confirmed findings from the mining-tab audit (10 Medium, 12 Low; 0 Critical/High), adversarially reviewed (6 follow-ups found + fixed, incl. the review-caught idle-auto-start bypass and a wrong benchmark-restore condition). Crash-safety & lifecycle: - M-04: join a stale/finished monitor thread in XmrigManager::start() and ~XmrigManager so an xmrig crash-then-restart (or quit) no longer std::terminate()s the wallet. - L-03/L-10: surface an unexpected miner exit once and clear the stale running flag. UI never blocks (M-03/L-06/L-08/L-09/L-13): pool start/stop now run on a dedicated serialized FIFO mining-control thread (joined before teardown), so the ~13 call sites don't block the render thread on stop()'s SIGTERM->SIGKILL->join; the spawn result marshals back to the UI. Miner-process / pool trust boundary: - M-01: validate the payout address (util::isValidRecipientAddress) at EVERY start path — the UI gate AND App::startPoolMining() (idle auto-start / thread scaling) — so a stale/wrong-chain address can't silently lose rewards. - M-09: SSRF guard skips the background pool-stats GET for loopback/private/link-local/single-label hosts. - M-02/L-02: cap the pool-stats + xmrig-API HTTP response bodies. - L-01: write the xmrig config 0600 at creation (POSIX open with mode) — no world/group-readable window. - M-10: reject shell-metacharacter binary paths before the version popen (excluding '()' so Program Files (x86) still works). Solo mining: M-06/M-08 clamp thread count to [1, cores] at the setgenerate/xmrig boundary; M-07 notify + don't lie on stop failure. Correctness: L-05 block-time constant 75->150s (chainparams); M-05 discloses pool-mode "Est. Daily" as a rough solo-equivalent; L-04/L-11/L-12 benchmark lifecycle (cancel on nav-away / mode-switch with restore, skip rebalance mid-benchmark); L-07 honor cancel mid-extract in both the xmrig and daemon updaters. Two new i18n keys back-filled across all 8 languages; CJK subset font rebuilt for the new glyphs. Verified across full-node, lite, and Windows builds; tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
2.8 KiB
C++
88 lines
2.8 KiB
C++
#include "mining_tab_helpers.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <thread>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
int GetMaxMiningThreads()
|
|
{
|
|
int hardwareThreads = static_cast<int>(std::thread::hardware_concurrency());
|
|
return std::max(1, hardwareThreads);
|
|
}
|
|
|
|
int ClampMiningThreads(int requestedThreads, int maxThreads)
|
|
{
|
|
int boundedMax = std::max(1, maxThreads);
|
|
return std::clamp(requestedThreads, 1, boundedMax);
|
|
}
|
|
|
|
bool IsPoolMiningActive(bool poolMode, bool xmrigRunning, bool soloMiningRunning)
|
|
{
|
|
return poolMode ? xmrigRunning : (soloMiningRunning || xmrigRunning);
|
|
}
|
|
|
|
std::string FormatHashrate(double hashrate)
|
|
{
|
|
char buffer[64];
|
|
if (hashrate >= 1e12) {
|
|
snprintf(buffer, sizeof(buffer), "%.2f TH/s", hashrate / 1e12);
|
|
} else if (hashrate >= 1e9) {
|
|
snprintf(buffer, sizeof(buffer), "%.2f GH/s", hashrate / 1e9);
|
|
} else if (hashrate >= 1e6) {
|
|
snprintf(buffer, sizeof(buffer), "%.2f MH/s", hashrate / 1e6);
|
|
} else if (hashrate >= 1e3) {
|
|
snprintf(buffer, sizeof(buffer), "%.2f KH/s", hashrate / 1e3);
|
|
} else {
|
|
snprintf(buffer, sizeof(buffer), "%.2f H/s", hashrate);
|
|
}
|
|
return std::string(buffer);
|
|
}
|
|
|
|
std::string FormatFeePercent(double feePercent)
|
|
{
|
|
// Whole fees read "1"; fractional ones keep only their significant decimals
|
|
// ("1.5", "0.9", "1.25") with no trailing zeros. Capped at 2 dp — finer than
|
|
// any pool advertises, and the caller appends the "%".
|
|
char buffer[32];
|
|
snprintf(buffer, sizeof(buffer), "%.2f", feePercent);
|
|
std::string s(buffer);
|
|
if (s.find('.') != std::string::npos) {
|
|
s.erase(s.find_last_not_of('0') + 1);
|
|
if (!s.empty() && s.back() == '.') s.pop_back();
|
|
}
|
|
return s;
|
|
}
|
|
|
|
double EstimateHoursToBlock(double localHashrate, double networkHashrate, double difficulty)
|
|
{
|
|
(void)difficulty;
|
|
if (localHashrate <= 0.0 || networkHashrate <= 0.0) return 0.0;
|
|
double blocksPerHour = 3600.0 / 150.0; // DragonX mainnet target spacing is 150s (chainparams) (L-05)
|
|
double share = localHashrate / networkHashrate;
|
|
if (share <= 0.0) return 0.0;
|
|
return 1.0 / (blocksPerHour * share);
|
|
}
|
|
|
|
std::string FormatEstTime(double estimatedHours)
|
|
{
|
|
char buffer[64];
|
|
if (estimatedHours <= 0.0) {
|
|
return "N/A";
|
|
} else if (estimatedHours < 1.0) {
|
|
snprintf(buffer, sizeof(buffer), "~%.0f min", estimatedHours * 60.0);
|
|
} else if (estimatedHours < 24.0) {
|
|
snprintf(buffer, sizeof(buffer), "~%.1f hrs", estimatedHours);
|
|
} else if (estimatedHours < 168.0) {
|
|
snprintf(buffer, sizeof(buffer), "~%.1f days", estimatedHours / 24.0);
|
|
} else {
|
|
snprintf(buffer, sizeof(buffer), "~%.1f weeks", estimatedHours / 168.0);
|
|
}
|
|
return std::string(buffer);
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|