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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user