daemon version check, idle mining control, bootstrap mirror, import key paste, and cleanup

- Add startup binary version checking for dragonxd/xmrig
- Display daemon version in UI
- Add idle mining thread count adjustment
- Add bootstrap mirror option (bootstrap2.dragonx.is) in setup wizard
- Add paste button to import private key dialog with address validation
- Add z-address generation UI feedback (loading indicator)
- Add option to delete blockchain data while preserving wallet.dat
- Add font scale slider hotkey tooltip (Ctrl+Plus/Ctrl+Minus)
- Fix Windows RPC auth: trim \r from config values, add .cookie fallback
- Fix connection status message during block index loading
- Improve application shutdown to prevent lingering background process
This commit is contained in:
dan_s
2026-03-17 14:57:12 -05:00
parent f0c87e4092
commit 4a841fd032
27 changed files with 897 additions and 2050 deletions

View File

@@ -138,6 +138,37 @@ void App::tryConnect()
// endlessly — tell the user what's wrong.
bool authFailure = (connectErr.find("401") != std::string::npos);
if (authFailure) {
// Try .cookie auth as fallback — the daemon may have
// generated a .cookie file instead of using DRAGONX.conf credentials
std::string dataDir = rpc::Connection::getDefaultDataDir();
std::string cookieUser, cookiePass;
if (rpc::Connection::readAuthCookie(dataDir, cookieUser, cookiePass)) {
VERBOSE_LOGF("[connect #%d] HTTP 401 — retrying with .cookie auth from %s\n",
attempt, dataDir.c_str());
worker_->post([this, config, cookieUser, cookiePass, attempt]() -> rpc::RPCWorker::MainCb {
auto cookieConfig = config;
cookieConfig.rpcuser = cookieUser;
cookieConfig.rpcpassword = cookiePass;
bool ok = rpc_->connect(cookieConfig.host, cookieConfig.port, cookieConfig.rpcuser, cookieConfig.rpcpassword);
return [this, cookieConfig, ok, attempt]() {
connection_in_progress_ = false;
if (ok) {
VERBOSE_LOGF("[connect #%d] Connected via .cookie auth\n", attempt);
saved_config_ = cookieConfig;
onConnected();
} else {
state_.connected = false;
connection_status_ = "Auth failed — check rpcuser/rpcpassword";
VERBOSE_LOGF("[connect #%d] .cookie auth also failed\n", attempt);
ui::Notifications::instance().error(
"RPC authentication failed (HTTP 401). "
"The rpcuser/rpcpassword in DRAGONX.conf don't match the running daemon. "
"Restart the daemon or correct the credentials.");
}
};
});
return; // async retry in progress
}
state_.connected = false;
std::string confPath = rpc::Connection::getDefaultConfPath();
connection_status_ = "Auth failed — check rpcuser/rpcpassword";
@@ -160,6 +191,17 @@ void App::tryConnect()
connection_status_ = "Connecting to daemon...";
VERBOSE_LOGF("[connect #%d] RPC connection failed — external daemon on port but RPC not ready yet, will retry...\n", attempt);
refresh_timer_ = REFRESH_INTERVAL - 1.0f;
} else if (connectErr.find("Loading") != std::string::npos ||
connectErr.find("Verifying") != std::string::npos ||
connectErr.find("Activating") != std::string::npos ||
connectErr.find("Rewinding") != std::string::npos ||
connectErr.find("Rescanning") != std::string::npos ||
connectErr.find("Pruning") != std::string::npos) {
// Daemon is reachable but still in warmup (Loading block index, etc.)
state_.connected = false;
connection_status_ = connectErr;
VERBOSE_LOGF("[connect #%d] Daemon warmup: %s\n", attempt, connectErr.c_str());
refresh_timer_ = REFRESH_INTERVAL - 1.0f;
} else {
onDisconnected("Connection failed");
VERBOSE_LOGF("[connect #%d] RPC connection failed — no daemon starting, no external detected\n", attempt);
@@ -230,12 +272,18 @@ void App::onConnected()
state_.protocol_version = info["protocolversion"].get<int>();
if (info.contains("p2pport"))
state_.p2p_port = info["p2pport"].get<int>();
if (info.contains("longestchain"))
state_.longestchain = info["longestchain"].get<int>();
if (info.contains("longestchain")) {
int lc = info["longestchain"].get<int>();
// Don't regress to 0 — daemon returns 0 when peers haven't been polled
if (lc > 0) state_.longestchain = lc;
}
if (info.contains("notarized"))
state_.notarized = info["notarized"].get<int>();
if (info.contains("blocks"))
state_.sync.blocks = info["blocks"].get<int>();
// longestchain can lag behind blocks when peer data is stale
if (state_.longestchain > 0 && state_.sync.blocks > state_.longestchain)
state_.longestchain = state_.sync.blocks;
} catch (const std::exception& e) {
DEBUG_LOGF("[onConnected] getinfo callback error: %s\n", e.what());
}
@@ -742,8 +790,14 @@ void App::refreshData()
state_.sync.headers = blockInfo["headers"].get<int>();
if (blockInfo.contains("verificationprogress"))
state_.sync.verification_progress = blockInfo["verificationprogress"].get<double>();
if (blockInfo.contains("longestchain"))
state_.longestchain = blockInfo["longestchain"].get<int>();
if (blockInfo.contains("longestchain")) {
int lc = blockInfo["longestchain"].get<int>();
// Don't regress to 0 — daemon returns 0 when peers haven't been polled
if (lc > 0) state_.longestchain = lc;
}
// longestchain can lag behind blocks when peer data is stale
if (state_.longestchain > 0 && state_.sync.blocks > state_.longestchain)
state_.longestchain = state_.sync.blocks;
// Use longestchain (actual network tip) for sync check when available,
// since headers can be inflated by misbehaving peers.
if (state_.longestchain > 0)
@@ -891,8 +945,14 @@ void App::refreshBalance()
state_.sync.headers = blockInfo["headers"].get<int>();
if (blockInfo.contains("verificationprogress"))
state_.sync.verification_progress = blockInfo["verificationprogress"].get<double>();
if (blockInfo.contains("longestchain"))
state_.longestchain = blockInfo["longestchain"].get<int>();
if (blockInfo.contains("longestchain")) {
int lc = blockInfo["longestchain"].get<int>();
// Don't regress to 0 — daemon returns 0 when peers haven't been polled
if (lc > 0) state_.longestchain = lc;
}
// longestchain can lag behind blocks when peer data is stale
if (state_.longestchain > 0 && state_.sync.blocks > state_.longestchain)
state_.longestchain = state_.sync.blocks;
if (state_.longestchain > 0)
state_.sync.syncing = (state_.sync.blocks < state_.longestchain - 2);
else
@@ -1358,7 +1418,7 @@ void App::startPoolMining(int threads)
if (cfg.wallet_address.empty()) {
DEBUG_LOGF("[ERROR] Pool mining: No wallet address available\n");
ui::Notifications::instance().error("No wallet address available for pool mining");
ui::Notifications::instance().error("No wallet address available — generate a Z address in the Receive tab");
return;
}