feat(mining): auto-balance driver, per-pool algo, live miner version
- app: periodic weighted-random auto-balance of the pool while pool-mining (~30-min cadence; restarts the miner only when the pick actually changes), plus snapshot + refresh accessors for the UI. - app_network: resolve the xmrig algo per pool at start (pool.dragonx.cc = rx/dragonx, pool.dragonx.is = rx/hush; custom hosts keep the setting). - xmrig_manager: schema-aware pool-side hashrate readout (fixes a silent 0 for Miningcore pools); expose the running miner's version from its HTTP API and a cached `xmrig --version` detection so the UI can show it before mining. - wallet_state: carry the running miner's version. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
99
src/app.cpp
99
src/app.cpp
@@ -22,6 +22,7 @@
|
||||
#include "daemon/embedded_daemon.h"
|
||||
#include "daemon/lifecycle_adapters.h"
|
||||
#include "daemon/xmrig_manager.h"
|
||||
#include "util/pool_registry.h"
|
||||
#include "ui/windows/main_window.h"
|
||||
#include "ui/windows/balance_tab.h"
|
||||
#include "ui/windows/send_tab.h"
|
||||
@@ -109,9 +110,101 @@ namespace dragonx {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
App::App() = default;
|
||||
App::App()
|
||||
{
|
||||
// Seed the auto-balance RNG once per run so weighted-random pool selection isn't
|
||||
// deterministic across launches.
|
||||
balance_rng_.seed(std::random_device{}());
|
||||
}
|
||||
App::~App() = default;
|
||||
|
||||
namespace {
|
||||
// How often auto-balance re-evaluates the pool while active. Long, because switching
|
||||
// restarts the miner (drops in-flight shares + reconnect); the incumbent stickiness
|
||||
// in chooseWeightedPool makes actual switches rarer still.
|
||||
constexpr long long kRebalanceIntervalMs = 30LL * 60LL * 1000LL; // 30 minutes
|
||||
|
||||
long long steadyNowMs()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void App::updatePoolAutoBalance()
|
||||
{
|
||||
if (!settings_) return;
|
||||
if (!supportsPoolMining()) return; // pool mining is full-node only
|
||||
if (settings_->getPoolSelectMode() != config::Settings::PoolSelectMode::AutoBalance) return;
|
||||
if (!settings_->getPoolMode()) return; // only while POOL mode is selected
|
||||
|
||||
const long long now = steadyNowMs();
|
||||
const bool intervalDue = (last_balance_eval_ms_ == 0) ||
|
||||
(now - last_balance_eval_ms_ >= kRebalanceIntervalMs);
|
||||
|
||||
// Kick a background refresh when requested or due (never while one is in flight).
|
||||
if ((balance_refresh_pending_ || intervalDue) && !pool_stats_service_.busy()) {
|
||||
balance_refresh_pending_ = false;
|
||||
last_balance_eval_ms_ = now;
|
||||
balance_snapshot_seen_ = false;
|
||||
pool_stats_service_.refresh(util::knownPools());
|
||||
return; // results land on a later frame
|
||||
}
|
||||
|
||||
// Apply the freshly-completed snapshot exactly once.
|
||||
if (!pool_stats_service_.busy()) {
|
||||
const auto snap = pool_stats_service_.snapshot();
|
||||
if (snap.ready && !balance_snapshot_seen_) {
|
||||
balance_snapshot_seen_ = true;
|
||||
applyPoolAutoBalance(snap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::applyPoolAutoBalance(const util::PoolStatsService::Snapshot& snap)
|
||||
{
|
||||
const auto& pools = util::knownPools();
|
||||
|
||||
std::vector<util::PoolHashrate> samples;
|
||||
samples.reserve(pools.size());
|
||||
for (const auto& kp : pools) {
|
||||
const auto it = snap.byId.find(kp.id);
|
||||
samples.push_back(it != snap.byId.end()
|
||||
? it->second
|
||||
: util::PoolHashrate{kp.id, 0.0, false});
|
||||
}
|
||||
|
||||
const util::KnownPool* current = util::findKnownPoolByUrl(settings_->getPoolUrl());
|
||||
const std::string currentId = current ? current->id : std::string{};
|
||||
|
||||
const std::string chosenId = util::chooseWeightedPool(samples, currentId, balance_rng_);
|
||||
if (chosenId.empty() || chosenId == currentId) return; // keep the current pool
|
||||
|
||||
const util::KnownPool* chosen = nullptr;
|
||||
for (const auto& kp : pools)
|
||||
if (kp.id == chosenId) { chosen = &kp; break; }
|
||||
if (!chosen) return;
|
||||
|
||||
settings_->setPoolUrl(chosen->stratum);
|
||||
settings_->save();
|
||||
|
||||
ui::Notifications::instance().info(
|
||||
std::string(TR("mining_auto_balanced_to")) + " " + chosen->label);
|
||||
|
||||
// Restart so the new pool (and its per-pool algo) takes effect immediately.
|
||||
if (xmrig_manager_ && xmrig_manager_->isRunning()) {
|
||||
const int threads = xmrig_manager_->getRequestedThreads();
|
||||
stopPoolMining();
|
||||
startPoolMining(threads);
|
||||
}
|
||||
}
|
||||
|
||||
std::string App::poolMiningInstalledVersion()
|
||||
{
|
||||
daemon::XmrigManager::startVersionDetection(); // idempotent one-shot
|
||||
return daemon::XmrigManager::installedVersion();
|
||||
}
|
||||
|
||||
bool App::sendStopCommandSafely(rpc::RPCClient& client, const char* context)
|
||||
{
|
||||
const char* label = context ? context : "App";
|
||||
@@ -706,6 +799,7 @@ void App::update()
|
||||
ps.pool_diff = xs.pool_diff;
|
||||
ps.pool_url = xs.pool_url;
|
||||
ps.algo = xs.algo;
|
||||
ps.version = xs.version;
|
||||
ps.connected = xs.connected;
|
||||
// Get memory directly from OS (more reliable than API)
|
||||
double memMB = xmrig_manager_->getMemoryUsageMB();
|
||||
@@ -723,6 +817,9 @@ void App::update()
|
||||
state_.pool_mining.xmrig_running = false;
|
||||
}
|
||||
|
||||
// Auto-balance: periodically re-pick the pool by hashrate (self-throttled).
|
||||
updatePoolAutoBalance();
|
||||
|
||||
// Populate solo mining log lines from daemon output
|
||||
if (daemon_controller_ && daemon_controller_->isRunning()) {
|
||||
state_.mining.log_lines = daemon_controller_->recentLines(50);
|
||||
|
||||
Reference in New Issue
Block a user