feat(wallet): confirm before stopping an adopted node to switch wallets

When switching wallets, the node the app would restart may be one this session
ADOPTED — a dragonxd already running at launch (left up by "keep node running",
or started by the user). Rather than stopping it silently (or the old dead-code
"stop any external dragonxd first" refusal), show a "Stop the running node?"
confirmation first; switching then stops it (RPC stop + wait for the port to
fully free) and relaunches on the selected wallet.

- switchToWallet(walletFile, stopDaemonConfirmed=false): when the node is adopted
  (externalDaemonDetected) and not yet confirmed, defer to the dialog and return.
- renderSwitchStopDaemonDialog(): BlurFloat overlay (house style) with a warning
  header; confirm re-enters switchToWallet(w, true); cancel/X aborts, node keeps
  running. Owned nodes (started this session) still restart silently.
- i18n (EN + 8 languages, additive) + CJK subset rebuild; modal-switch-stopnode
  sweep surface for visual review.

Gate/re-entrancy adversarially verified (no state leak; owned switches ungated;
prompt reappears on a reverted switch; dead-daemon-before-confirm handled).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 19:17:08 -05:00
parent 30bc80b2f2
commit 42d59709f9
14 changed files with 97 additions and 3 deletions

View File

@@ -488,7 +488,9 @@ public:
// Switch the active wallet: persist the new -wallet=<name>, stop the node, restart on it
// (rescan only if it was never synced in this datadir). Per-wallet data follows automatically
// via the identity-scoped caches (P1). No-op if that wallet is already active.
void switchToWallet(const std::string& walletFile);
// stopDaemonConfirmed: skip the "stop the running node?" confirmation (set true when re-entered from
// that dialog). The confirmation only appears when the node to restart was ADOPTED (externally running).
void switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed = false);
// Main-thread continuation: if a wallet switch's daemon failed to start, revert active_wallet_file.
void processWalletSwitchRevert();
@@ -762,6 +764,10 @@ private:
// loop flags a revert so a broken wallet still can't stick.
std::atomic<bool> wallet_switch_pending_confirm_{false};
std::string wallet_switch_prev_file_;
// Confirm-before-stop for a switch that would stop an ADOPTED (externally-running) node. switchToWallet
// sets these and defers to renderSwitchStopDaemonDialog; confirming re-calls switchToWallet(w, true).
bool show_switch_stop_daemon_confirm_ = false;
std::string pending_switch_wallet_file_;
// Set by the deleteBlockchainData worker (item count); the main loop surfaces a completion toast
// and resets it to -1. Atomic because the worker thread writes it and the UI thread reads/clears it.
@@ -1163,6 +1169,7 @@ private:
void renderDecryptWalletDialog();
void renderPinDialogs();
void renderAntivirusHelpDialog();
void renderSwitchStopDaemonDialog(); // confirm before stopping an adopted node to switch wallets
void processDeferredEncryption();
// Private methods - connection