improve diagnostics, security UX, and network tab refresh
Diagnostics & logging: - add verbose logging system (VERBOSE_LOGF) with toggle in Settings - forward app-level log messages to Console tab for in-UI visibility - add detailed connection attempt logging (attempt #, daemon state, config paths, auth failures, port owner identification) - detect HTTP 401 auth failures and show actionable error messages - identify port owner process (PID + name) on both Linux and Windows - demote noisy acrylic/shader traces from DEBUG_LOGF to VERBOSE_LOGF - persist verbose_logging preference in settings.json - link iphlpapi on Windows for GetExtendedTcpTable Security & encryption: - update local encryption state immediately after encryptwallet RPC so Settings reflects the change before daemon restarts - show notifications for encrypt success/failure and PIN skip - use dedicated RPC client for z_importwallet during decrypt flow to avoid blocking main rpc_ curl_mutex (which starved peer/tx refresh) - force full state refresh (addresses, transactions, peers) after successful wallet import Network tab: - redesign peers refresh button as glass-panel with icon + label, matching the mining button style - add spinning arc animation while peer data is loading (peer_refresh_in_progress_ atomic flag set/cleared in refreshPeerInfo) - prevent double-click spam during refresh - add refresh-button size to ui.toml Other: - use fast_rpc_ for rescan polling to avoid blocking on main rpc_ - enable DRAGONX_DEBUG in all build configs (was debug-only) - setup.sh: pull latest xmrig-hac when repo already exists
This commit is contained in:
@@ -32,6 +32,9 @@ void App::tryConnect()
|
||||
{
|
||||
if (connection_in_progress_) return;
|
||||
|
||||
static int connect_attempt = 0;
|
||||
++connect_attempt;
|
||||
|
||||
connection_in_progress_ = true;
|
||||
connection_status_ = "Loading configuration...";
|
||||
|
||||
@@ -40,12 +43,15 @@ void App::tryConnect()
|
||||
|
||||
if (config.rpcuser.empty() || config.rpcpassword.empty()) {
|
||||
connection_in_progress_ = false;
|
||||
DEBUG_LOGF("Could not find DRAGONX.conf or missing rpcuser/rpcpassword\n");
|
||||
std::string confPath = rpc::Connection::getDefaultConfPath();
|
||||
VERBOSE_LOGF("[connect #%d] No valid config — DRAGONX.conf missing or no rpcuser/rpcpassword (looked at: %s)\n",
|
||||
connect_attempt, confPath.c_str());
|
||||
|
||||
// If we already know an external daemon is on the port, just wait
|
||||
// for the config file to appear (the daemon creates it on first run).
|
||||
if (embedded_daemon_ && embedded_daemon_->externalDaemonDetected()) {
|
||||
connection_status_ = "Waiting for daemon config...";
|
||||
VERBOSE_LOGF("[connect #%d] External daemon detected on port, waiting for config file to appear\n", connect_attempt);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,22 +62,32 @@ void App::tryConnect()
|
||||
connection_status_ = "Starting dragonxd...";
|
||||
if (startEmbeddedDaemon()) {
|
||||
// Will retry connection after daemon starts
|
||||
DEBUG_LOGF("Embedded daemon starting, will retry connection...\n");
|
||||
VERBOSE_LOGF("[connect #%d] Embedded daemon starting, will retry connection...\n", connect_attempt);
|
||||
} else if (embedded_daemon_ && embedded_daemon_->externalDaemonDetected()) {
|
||||
connection_status_ = "Waiting for daemon config...";
|
||||
DEBUG_LOGF("External daemon detected but no config yet, will retry...\n");
|
||||
VERBOSE_LOGF("[connect #%d] External daemon detected but no config yet, will retry...\n", connect_attempt);
|
||||
} else {
|
||||
VERBOSE_LOGF("[connect #%d] startEmbeddedDaemon() failed — lastError: %s, binary: %s\n",
|
||||
connect_attempt,
|
||||
embedded_daemon_ ? embedded_daemon_->getLastError().c_str() : "(no daemon object)",
|
||||
daemon::EmbeddedDaemon::findDaemonBinary().c_str());
|
||||
}
|
||||
} else if (!use_embedded_daemon_) {
|
||||
VERBOSE_LOGF("[connect #%d] Embedded daemon disabled (using external). No config found at %s\n",
|
||||
connect_attempt, confPath.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
connection_status_ = "Connecting to dragonxd...";
|
||||
DEBUG_LOGF("Connecting to %s:%s\n", config.host.c_str(), config.port.c_str());
|
||||
VERBOSE_LOGF("[connect #%d] Connecting to %s:%s (user=%s)\n",
|
||||
connect_attempt, config.host.c_str(), config.port.c_str(), config.rpcuser.c_str());
|
||||
|
||||
// Run the blocking rpc_->connect() on the worker thread so the UI
|
||||
// stays responsive (curl connect timeout can be up to 10 seconds).
|
||||
if (!worker_) {
|
||||
connection_in_progress_ = false;
|
||||
VERBOSE_LOGF("[connect #%d] No worker thread available!\n", connect_attempt);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,41 +96,90 @@ void App::tryConnect()
|
||||
(embedded_daemon_->getState() == daemon::EmbeddedDaemon::State::Starting ||
|
||||
embedded_daemon_->getState() == daemon::EmbeddedDaemon::State::Running);
|
||||
bool externalDetected = embedded_daemon_ && embedded_daemon_->externalDaemonDetected();
|
||||
int attempt = connect_attempt;
|
||||
|
||||
worker_->post([this, config, daemonStarting, externalDetected]() -> rpc::RPCWorker::MainCb {
|
||||
// Log detailed daemon state for diagnostics
|
||||
if (embedded_daemon_) {
|
||||
const char* stateStr = "unknown";
|
||||
switch (embedded_daemon_->getState()) {
|
||||
case daemon::EmbeddedDaemon::State::Stopped: stateStr = "Stopped"; break;
|
||||
case daemon::EmbeddedDaemon::State::Starting: stateStr = "Starting"; break;
|
||||
case daemon::EmbeddedDaemon::State::Running: stateStr = "Running"; break;
|
||||
case daemon::EmbeddedDaemon::State::Stopping: stateStr = "Stopping"; break;
|
||||
case daemon::EmbeddedDaemon::State::Error: stateStr = "Error"; break;
|
||||
}
|
||||
VERBOSE_LOGF("[connect #%d] Daemon state: %s, running: %s, external: %s, crashes: %d, lastErr: %s\n",
|
||||
attempt, stateStr,
|
||||
embedded_daemon_->isRunning() ? "yes" : "no",
|
||||
externalDetected ? "yes" : "no",
|
||||
embedded_daemon_->getCrashCount(),
|
||||
embedded_daemon_->getLastError().empty() ? "(none)" : embedded_daemon_->getLastError().c_str());
|
||||
} else {
|
||||
VERBOSE_LOGF("[connect #%d] No embedded daemon object (use_embedded=%s)\n",
|
||||
attempt, use_embedded_daemon_ ? "yes" : "no");
|
||||
}
|
||||
|
||||
worker_->post([this, config, daemonStarting, externalDetected, attempt]() -> rpc::RPCWorker::MainCb {
|
||||
bool connected = rpc_->connect(config.host, config.port, config.rpcuser, config.rpcpassword);
|
||||
std::string connectErr = rpc_->getLastConnectError();
|
||||
|
||||
return [this, config, connected, daemonStarting, externalDetected]() {
|
||||
return [this, config, connected, daemonStarting, externalDetected, attempt, connectErr]() {
|
||||
if (connected) {
|
||||
VERBOSE_LOGF("[connect #%d] Connected successfully\n", attempt);
|
||||
saved_config_ = config; // save for fast-lane connection
|
||||
onConnected();
|
||||
} else {
|
||||
if (daemonStarting) {
|
||||
// HTTP 401 = authentication failure. The daemon is running
|
||||
// but our rpcuser/rpcpassword don't match. Don't retry
|
||||
// endlessly — tell the user what's wrong.
|
||||
bool authFailure = (connectErr.find("401") != std::string::npos);
|
||||
if (authFailure) {
|
||||
state_.connected = false;
|
||||
std::string confPath = rpc::Connection::getDefaultConfPath();
|
||||
connection_status_ = "Auth failed — check rpcuser/rpcpassword";
|
||||
VERBOSE_LOGF("[connect #%d] HTTP 401 — rpcuser/rpcpassword in %s don't match the daemon. "
|
||||
"Edit the file or restart the daemon to regenerate credentials.\n",
|
||||
attempt, confPath.c_str());
|
||||
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.");
|
||||
} else if (daemonStarting) {
|
||||
state_.connected = false;
|
||||
connection_status_ = "Waiting for dragonxd to start...";
|
||||
DEBUG_LOGF("Connection attempt failed — daemon still starting, will retry...\n");
|
||||
VERBOSE_LOGF("[connect #%d] RPC connection failed — daemon still starting, will retry...\n", attempt);
|
||||
} else if (externalDetected) {
|
||||
state_.connected = false;
|
||||
connection_status_ = "Connecting to daemon...";
|
||||
DEBUG_LOGF("External daemon on port but RPC not ready yet, will retry...\n");
|
||||
VERBOSE_LOGF("[connect #%d] RPC connection failed — external daemon on port but RPC not ready yet, will retry...\n", attempt);
|
||||
} else {
|
||||
onDisconnected("Connection failed");
|
||||
VERBOSE_LOGF("[connect #%d] RPC connection failed — no daemon starting, no external detected\n", attempt);
|
||||
|
||||
if (use_embedded_daemon_ && !isEmbeddedDaemonRunning()) {
|
||||
// Prevent infinite crash-restart loop
|
||||
if (embedded_daemon_ && embedded_daemon_->getCrashCount() >= 3) {
|
||||
connection_status_ = "Daemon crashed " + std::to_string(embedded_daemon_->getCrashCount()) + " times";
|
||||
DEBUG_LOGF("Daemon crashed %d times — not restarting (use Settings > Restart Daemon to retry)\n",
|
||||
embedded_daemon_->getCrashCount());
|
||||
VERBOSE_LOGF("[connect #%d] Daemon crashed %d times — not restarting (use Settings > Restart Daemon to retry)\n",
|
||||
attempt, embedded_daemon_->getCrashCount());
|
||||
} else {
|
||||
connection_status_ = "Starting dragonxd...";
|
||||
if (startEmbeddedDaemon()) {
|
||||
DEBUG_LOGF("Embedded daemon starting, will retry connection...\n");
|
||||
VERBOSE_LOGF("[connect #%d] Embedded daemon starting, will retry connection...\n", attempt);
|
||||
} else if (embedded_daemon_ && embedded_daemon_->externalDaemonDetected()) {
|
||||
connection_status_ = "Connecting to daemon...";
|
||||
DEBUG_LOGF("External daemon detected, will connect via RPC...\n");
|
||||
VERBOSE_LOGF("[connect #%d] External daemon detected, will connect via RPC...\n", attempt);
|
||||
} else {
|
||||
VERBOSE_LOGF("[connect #%d] Failed to start embedded daemon — lastError: %s\n",
|
||||
attempt,
|
||||
embedded_daemon_ ? embedded_daemon_->getLastError().c_str() : "(no daemon object)");
|
||||
}
|
||||
}
|
||||
} else if (!use_embedded_daemon_) {
|
||||
VERBOSE_LOGF("[connect #%d] Embedded daemon disabled — external daemon at %s:%s not responding\n",
|
||||
attempt, config.host.c_str(), config.port.c_str());
|
||||
} else {
|
||||
VERBOSE_LOGF("[connect #%d] Embedded daemon is running but RPC failed — daemon may be initializing\n", attempt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1033,6 +1098,8 @@ void App::refreshPeerInfo()
|
||||
auto* r = (fast_rpc_ && fast_rpc_->isConnected()) ? fast_rpc_.get() : rpc_.get();
|
||||
if (!w) return;
|
||||
|
||||
peer_refresh_in_progress_.store(true, std::memory_order_relaxed);
|
||||
|
||||
w->post([this, r]() -> rpc::RPCWorker::MainCb {
|
||||
std::vector<PeerInfo> peers;
|
||||
std::vector<BannedPeer> bannedPeers;
|
||||
@@ -1079,6 +1146,7 @@ void App::refreshPeerInfo()
|
||||
state_.peers = std::move(peers);
|
||||
state_.bannedPeers = std::move(bannedPeers);
|
||||
state_.last_peer_update = std::time(nullptr);
|
||||
peer_refresh_in_progress_.store(false, std::memory_order_relaxed);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user