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

@@ -118,13 +118,16 @@ ConnectionConfig Connection::parseConfFile(const std::string& path)
std::string key = line.substr(0, eq_pos);
std::string value = line.substr(eq_pos + 1);
// Trim whitespace
while (!key.empty() && (key.back() == ' ' || key.back() == '\t')) {
// Trim whitespace (including \r from Windows line endings)
while (!key.empty() && (key.back() == ' ' || key.back() == '\t' || key.back() == '\r')) {
key.pop_back();
}
while (!value.empty() && (value[0] == ' ' || value[0] == '\t')) {
value.erase(0, 1);
}
while (!value.empty() && (value.back() == ' ' || value.back() == '\t' || value.back() == '\r')) {
value.pop_back();
}
// Map to config
if (key == "rpcuser") {
@@ -172,6 +175,16 @@ ConnectionConfig Connection::autoDetectConfig()
}
}
// If rpcpassword is empty, the daemon may be using .cookie auth
if (config.rpcpassword.empty()) {
std::string cookieUser, cookiePass;
if (readAuthCookie(data_dir, cookieUser, cookiePass)) {
config.rpcuser = cookieUser;
config.rpcpassword = cookiePass;
DEBUG_LOGF("Using .cookie authentication (no rpcpassword in config)\n");
}
}
// Set defaults for missing values
if (config.host.empty()) {
config.host = DRAGONX_DEFAULT_RPC_HOST;
@@ -319,5 +332,40 @@ bool Connection::ensureEncryptionEnabled(const std::string& confPath)
return true;
}
bool Connection::readAuthCookie(const std::string& dataDir, std::string& user, std::string& password)
{
if (dataDir.empty()) return false;
#ifdef _WIN32
std::string cookiePath = dataDir + "\\.cookie";
#else
std::string cookiePath = dataDir + "/.cookie";
#endif
std::ifstream file(cookiePath);
if (!file.is_open()) return false;
std::string cookie;
std::getline(file, cookie);
file.close();
// Cookie format: __cookie__:base64encodedpassword
size_t colonPos = cookie.find(':');
if (colonPos == std::string::npos || colonPos == 0) return false;
user = cookie.substr(0, colonPos);
password = cookie.substr(colonPos + 1);
// Trim \r if present (Windows line endings)
while (!password.empty() && (password.back() == '\r' || password.back() == '\n')) {
password.pop_back();
}
if (user.empty() || password.empty()) return false;
DEBUG_LOGF("Read auth cookie from: %s (user=%s)\n", cookiePath.c_str(), user.c_str());
return true;
}
} // namespace rpc
} // namespace dragonx

View File

@@ -87,6 +87,15 @@ public:
*/
static bool ensureEncryptionEnabled(const std::string& confPath);
/**
* @brief Try to read .cookie auth file from the data directory
* @param dataDir Path to the daemon data directory
* @param user Output: cookie username (__cookie__)
* @param password Output: cookie password
* @return true if cookie file was read successfully
*/
static bool readAuthCookie(const std::string& dataDir, std::string& user, std::string& password);
private:
};

View File

@@ -100,7 +100,20 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
}
} catch (const std::exception& e) {
last_connect_error_ = e.what();
DEBUG_LOGF("Connection failed: %s\n", e.what());
// Daemon warmup messages (Loading block index, Verifying blocks, etc.)
// are normal startup progress — don't label them "Connection failed".
std::string msg = e.what();
bool isWarmup = (msg.find("Loading") != std::string::npos ||
msg.find("Verifying") != std::string::npos ||
msg.find("Activating") != std::string::npos ||
msg.find("Rewinding") != std::string::npos ||
msg.find("Rescanning") != std::string::npos ||
msg.find("Pruning") != std::string::npos);
if (isWarmup) {
DEBUG_LOGF("Daemon starting: %s\n", msg.c_str());
} else {
DEBUG_LOGF("Connection failed: %s\n", msg.c_str());
}
}
connected_ = false;