feat(network): a clear auth-failed node banner with a fix-it action

When the node rejects the wallet's RPC login (bad rpcuser/rpcpassword), the
status bar showed developer jargon ("Auth failed — check rpcuser/rpcpassword")
and the only action was Reconnect, which just repeats the failure. Now a
dedicated AuthFailed banner reason surfaces "Wallet can't sign in to the node"
with a plain-language detail and a real fix: Restart node for the embedded
daemon (regenerates its cookie creds) or Open config folder for an external one
(to fix DRAGONX.conf). Driven by a new rpc_auth_failed_ flag (set on the auth
path, cleared on each connect attempt), with evaluator unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 11:53:29 -05:00
co-authored by Claude Opus 4.8
parent adf61e43d6
commit 0101cc707c
6 changed files with 66 additions and 7 deletions
+22 -3
View File
@@ -19,9 +19,10 @@ enum class NodeBannerSeverity {
// What the banner's action button does. App maps this to the concrete call.
enum class NodeBannerAction {
None, // no button — nothing the user can usefully do from here
Reconnect, // full node: re-run the RPC connect state machine (App::tryConnect)
RestartNode, // full node: the embedded daemon crashed & auto-restart gave up (App::restartDaemon)
None, // no button — nothing the user can usefully do from here
Reconnect, // full node: re-run the RPC connect state machine (App::tryConnect)
RestartNode, // full node: the embedded daemon crashed & auto-restart gave up (App::restartDaemon)
OpenConfigFolder,// external node: open the folder holding DRAGONX.conf so the user can fix the login
};
// Why the banner is up. App maps this to a translated headline; `detail` carries the live,
@@ -33,6 +34,7 @@ enum class NodeBannerReason {
LiteOpenFailed, // lite build: the wallet failed to open
NoPeers, // connected to the daemon but no peer-derived tip (0 peers) — stalled, stale
NodeBusy, // the node is up but busy (rebuilding witnesses / catching up / not answering)
AuthFailed, // the node rejected the wallet's RPC login (bad rpcuser/rpcpassword in the conf)
};
struct NodeBannerState {
@@ -71,6 +73,8 @@ struct NodeBannerInputs {
bool node_busy = false;
std::string node_busy_detail;
bool auth_failed = false; // the node rejected the wallet's RPC login (bad rpcuser/rpcpassword)
std::string connection_status; // human-readable status line (already translated)
std::string daemon_last_error; // DaemonController::lastError() (may be empty)
std::string lite_open_error; // lite: last wallet-open failure reason
@@ -146,6 +150,21 @@ inline NodeBannerState evaluateNodeStatusBanner(const NodeBannerInputs& in) {
return s;
}
// The node rejected the wallet's RPC login — retrying won't help (the creds are wrong). Restarting
// the embedded node regenerates its cookie/creds; for an external node the user fixes DRAGONX.conf,
// so offer to open its folder. App renders a translated, actionable detail (connection_status here
// is the jargon "Auth failed…" line).
if (in.auth_failed) {
s.show = true;
s.severity = NodeBannerSeverity::Error;
s.reason = NodeBannerReason::AuthFailed;
s.action = (in.using_embedded_daemon && in.has_daemon_controller)
? NodeBannerAction::RestartNode
: NodeBannerAction::OpenConfigFolder;
s.detail.clear();
return s;
}
s.show = true;
s.severity = NodeBannerSeverity::Warning;
s.reason = NodeBannerReason::FullNodeOffline;