From 42d59709f950529f8c80b39872882cfecdde2769 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 15 Jul 2026 19:17:08 -0500 Subject: [PATCH] feat(wallet): confirm before stopping an adopted node to switch wallets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- res/fonts/NotoSansCJK-Subset.ttf | Bin 635568 -> 636016 bytes res/lang/de.json | 4 ++++ res/lang/es.json | 4 ++++ res/lang/fr.json | 4 ++++ res/lang/ja.json | 4 ++++ res/lang/ko.json | 4 ++++ res/lang/pt.json | 4 ++++ res/lang/ru.json | 4 ++++ res/lang/zh.json | 4 ++++ src/app.cpp | 37 +++++++++++++++++++++++++++++++ src/app.h | 9 +++++++- src/app_network.cpp | 11 ++++++++- src/app_sweep.cpp | 3 +++ src/util/i18n.cpp | 8 ++++++- 14 files changed, 97 insertions(+), 3 deletions(-) diff --git a/res/fonts/NotoSansCJK-Subset.ttf b/res/fonts/NotoSansCJK-Subset.ttf index 5a4acd68f58baff4af5e7d62977123fdba2a4dc9..a4c876b7aced6828a02089939061d3148579c1b2 100644 GIT binary patch delta 19048 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 W>X%H(+ZL(9g=`g*&umjqwf_e%Aht39 delta 18463 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 diff --git a/res/lang/de.json b/res/lang/de.json index ac20877..4bb9d79 100644 --- a/res/lang/de.json +++ b/res/lang/de.json @@ -1294,6 +1294,10 @@ "sweep_to": "Gefegt an:", "sweep_toggle": "In meine Wallet fegen (Schlüssel nicht behalten)", "sweep_tx": "Transaktion:", + "switch_stopnode_body": "Beim Wallet-Wechsel wird der Knoten mit der ausgewählten Wallet neu gestartet. Der laufende Knoten wird gestoppt und mit der neuen Wallet neu gestartet – wenn Sie ihn absichtlich laufen ließen, startet er automatisch wieder.", + "switch_stopnode_confirm": "Knoten stoppen & wechseln", + "switch_stopnode_title": "Laufenden Knoten stoppen?", + "switch_stopnode_warn": "Es läuft bereits ein Knoten, den diese Wallet nicht gestartet hat.", "syncing": "Synchronisiere...", "t_address": "T-Adresse", "t_addresses": "T-Adressen", diff --git a/res/lang/es.json b/res/lang/es.json index a877ba0..416d087 100644 --- a/res/lang/es.json +++ b/res/lang/es.json @@ -1294,6 +1294,10 @@ "sweep_to": "Barrido a:", "sweep_toggle": "Barrer a mi monedero (no conservar la clave)", "sweep_tx": "Transacción:", + "switch_stopnode_body": "Cambiar de cartera reinicia el nodo con la cartera seleccionada. El nodo en ejecución se detendrá y se reiniciará con la nueva cartera; si lo dejaste en marcha a propósito, volverá a iniciarse automáticamente.", + "switch_stopnode_confirm": "Detener nodo y cambiar", + "switch_stopnode_title": "¿Detener el nodo en ejecución?", + "switch_stopnode_warn": "Ya hay un nodo en ejecución que esta cartera no inició.", "syncing": "Sincronizando...", "t_address": "Dirección T", "t_addresses": "Direcciones T", diff --git a/res/lang/fr.json b/res/lang/fr.json index d537add..f9f6f91 100644 --- a/res/lang/fr.json +++ b/res/lang/fr.json @@ -1294,6 +1294,10 @@ "sweep_to": "Balayé vers :", "sweep_toggle": "Balayer vers mon portefeuille (ne pas conserver la clé)", "sweep_tx": "Transaction :", + "switch_stopnode_body": "Changer de portefeuille redémarre le nœud sur le portefeuille sélectionné. Le nœud en cours sera arrêté puis relancé sur le nouveau portefeuille — si vous l'avez laissé tourner exprès, il redémarre automatiquement.", + "switch_stopnode_confirm": "Arrêter le nœud et changer", + "switch_stopnode_title": "Arrêter le nœud en cours d'exécution ?", + "switch_stopnode_warn": "Un nœud que ce portefeuille n'a pas démarré est déjà en cours d'exécution.", "syncing": "Synchronisation...", "t_address": "Adresse T", "t_addresses": "Adresses T", diff --git a/res/lang/ja.json b/res/lang/ja.json index 0dfb124..88a83a3 100644 --- a/res/lang/ja.json +++ b/res/lang/ja.json @@ -1294,6 +1294,10 @@ "sweep_to": "集約先:", "sweep_toggle": "ウォレットに集約(鍵は保持しない)", "sweep_tx": "取引:", + "switch_stopnode_body": "ウォレットを切り替えると、選択したウォレットでノードが再起動します。実行中のノードは停止され、新しいウォレットで再起動されます。意図的に起動したままにしていた場合は、自動的に復帰します。", + "switch_stopnode_confirm": "ノードを停止して切り替え", + "switch_stopnode_title": "実行中のノードを停止しますか?", + "switch_stopnode_warn": "このウォレットが起動していないノードがすでに実行中です。", "syncing": "同期中...", "t_address": "Tアドレス", "t_addresses": "Tアドレス", diff --git a/res/lang/ko.json b/res/lang/ko.json index fb83487..bf08637 100644 --- a/res/lang/ko.json +++ b/res/lang/ko.json @@ -1294,6 +1294,10 @@ "sweep_to": "쓸어담은 주소:", "sweep_toggle": "내 지갑으로 쓸어담기 (키 보관 안 함)", "sweep_tx": "거래:", + "switch_stopnode_body": "지갑을 전환하면 선택한 지갑으로 노드가 다시 시작됩니다. 실행 중인 노드가 중지되고 새 지갑으로 다시 시작됩니다. 일부러 계속 실행해 두었다면 자동으로 다시 켜집니다.", + "switch_stopnode_confirm": "노드 중지 후 전환", + "switch_stopnode_title": "실행 중인 노드를 중지할까요?", + "switch_stopnode_warn": "이 지갑이 시작하지 않은 노드가 이미 실행 중입니다.", "syncing": "동기화 중...", "t_address": "T 주소", "t_addresses": "T 주소", diff --git a/res/lang/pt.json b/res/lang/pt.json index 8eb68c0..e9810c0 100644 --- a/res/lang/pt.json +++ b/res/lang/pt.json @@ -1294,6 +1294,10 @@ "sweep_to": "Varrido para:", "sweep_toggle": "Varrer para minha carteira (não manter a chave)", "sweep_tx": "Transação:", + "switch_stopnode_body": "Trocar de carteira reinicia o nó com a carteira selecionada. O nó em execução será parado e reiniciado com a nova carteira — se você o deixou em execução de propósito, ele volta automaticamente.", + "switch_stopnode_confirm": "Parar nó e trocar", + "switch_stopnode_title": "Parar o nó em execução?", + "switch_stopnode_warn": "Já há um nó em execução que esta carteira não iniciou.", "syncing": "Sincronizando...", "t_address": "Endereço T", "t_addresses": "Endereços T", diff --git a/res/lang/ru.json b/res/lang/ru.json index 23c69ba..8753c90 100644 --- a/res/lang/ru.json +++ b/res/lang/ru.json @@ -1294,6 +1294,10 @@ "sweep_to": "Переведено на:", "sweep_toggle": "Перевести в мой кошелёк (не сохранять ключ)", "sweep_tx": "Транзакция:", + "switch_stopnode_body": "Смена кошелька перезапускает узел с выбранным кошельком. Запущенный узел будет остановлен и перезапущен с новым кошельком — если вы намеренно оставили его работать, он запустится снова автоматически.", + "switch_stopnode_confirm": "Остановить узел и переключить", + "switch_stopnode_title": "Остановить запущенный узел?", + "switch_stopnode_warn": "Уже запущен узел, который не был запущен этим кошельком.", "syncing": "Синхронизация...", "t_address": "T-адрес", "t_addresses": "T-адреса", diff --git a/res/lang/zh.json b/res/lang/zh.json index c856b2f..abcb69f 100644 --- a/res/lang/zh.json +++ b/res/lang/zh.json @@ -1294,6 +1294,10 @@ "sweep_to": "归集到:", "sweep_toggle": "归集到我的钱包(不保留密钥)", "sweep_tx": "交易:", + "switch_stopnode_body": "切换钱包会以所选钱包重启节点。正在运行的节点将被停止并以新钱包重新启动——如果你是特意让它保持运行的,它会自动重新启动。", + "switch_stopnode_confirm": "停止节点并切换", + "switch_stopnode_title": "停止正在运行的节点?", + "switch_stopnode_warn": "已有一个并非由此钱包启动的节点正在运行。", "syncing": "同步中...", "t_address": "T 地址", "t_addresses": "T 地址", diff --git a/src/app.cpp b/src/app.cpp index 063cb76..7c661f1 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2034,6 +2034,7 @@ void App::render() renderEncryptWalletDialog(); renderDecryptWalletDialog(); renderPinDialogs(); + renderSwitchStopDaemonDialog(); // Render notifications (toast messages) ui::Notifications::instance().render(); @@ -3860,6 +3861,42 @@ void App::renderAntivirusHelpDialog() #endif } +void App::renderSwitchStopDaemonDialog() +{ + if (!show_switch_stop_daemon_confirm_) return; + + ui::material::OverlayDialogSpec ov; + ov.title = TR("switch_stopnode_title"); + ov.p_open = &show_switch_stop_daemon_confirm_; // the X / backdrop click acts as Cancel + ov.style = ui::material::OverlayStyle::BlurFloat; + ov.cardWidth = 520.0f; ov.idSuffix = "switchstopnode"; + if (!ui::material::BeginOverlayDialog(ov)) { + // Closed via X/backdrop — treat as cancel: drop the pending target. + if (!show_switch_stop_daemon_confirm_) pending_switch_wallet_file_.clear(); + return; + } + const float dp = ui::Layout::dpiScale(); + + ui::material::DialogWarningHeader(TR("switch_stopnode_warn")); + ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm())); + ImGui::TextWrapped("%s", TR("switch_stopnode_body")); + ImGui::Dummy(ImVec2(0, ui::Layout::spacingMd())); + + if (ui::material::TactileButton(TR("switch_stopnode_confirm"), ImVec2(200.0f * dp, 0))) { + const std::string w = pending_switch_wallet_file_; + pending_switch_wallet_file_.clear(); + show_switch_stop_daemon_confirm_ = false; + switchToWallet(w, /*stopDaemonConfirmed=*/true); // now proceeds to stop + wait + restart + } + ImGui::SameLine(); + if (ui::material::TactileButton(TR("cancel"), ImVec2(110.0f * dp, 0))) { + pending_switch_wallet_file_.clear(); + show_switch_stop_daemon_confirm_ = false; + } + + ui::material::EndOverlayDialog(); +} + void App::refreshNow() { // Trigger immediate refresh on all categories diff --git a/src/app.h b/src/app.h index 7b5b426..155542c 100644 --- a/src/app.h +++ b/src/app.h @@ -488,7 +488,9 @@ public: // Switch the active wallet: persist the new -wallet=, 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 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 diff --git a/src/app_network.cpp b/src/app_network.cpp index 216178b..d1c821a 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -1023,7 +1023,7 @@ void App::updateWalletIndexForActiveWallet(bool markOpened) if (wallet_index_.upsert(e)) wallet_index_.save(); } -void App::switchToWallet(const std::string& walletFile) +void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed) { if (!supportsFullNodeLifecycleActions()) { ui::Notifications::instance().warning("Full-node lifecycle actions are unavailable in lite build"); @@ -1056,6 +1056,15 @@ void App::switchToWallet(const std::string& walletFile) ui::Notifications::instance().warning("Finish or cancel the pending send before switching wallets."); return; } + // The node we'd restart may be one this session ADOPTED — a dragonxd already running when the wallet + // launched (left running by "keep node running", or started by the user). Switching must stop+restart + // it on the new wallet, so confirm first rather than killing a node the user may have kept up on + // purpose. (An owned node we started this session restarts silently, as before.) + if (!stopDaemonConfirmed && daemon_controller_ && daemon_controller_->externalDaemonDetected()) { + pending_switch_wallet_file_ = walletFile; + show_switch_stop_daemon_confirm_ = true; + return; + } // Rescan only if this wallet was never synced in this datadir (freshly imported/new). One we've // loaded here before just catches up from its recorded block on start — fast. diff --git a/src/app_sweep.cpp b/src/app_sweep.cpp index 7a82532..9d977c7 100644 --- a/src/app_sweep.cpp +++ b/src/app_sweep.cpp @@ -376,6 +376,9 @@ void App::buildSweepCatalog() add("modal-antivirus", ui::NavPage::Mining, [](App& a) { a.pending_antivirus_dialog_ = true; }, [](App& a) { a.pending_antivirus_dialog_ = false; }); + add("modal-switch-stopnode", ui::NavPage::Settings, + [](App& a) { a.pending_switch_wallet_file_ = "wallet-savings.dat"; a.show_switch_stop_daemon_confirm_ = true; }, + [](App& a) { a.show_switch_stop_daemon_confirm_ = false; a.pending_switch_wallet_file_.clear(); }); // Wave-2 fund/secret dialogs (setup never fires the async RPC — no button is clicked). add("modal-shield", ui::NavPage::Send, [](App&) { ui::ShieldDialog::showShieldCoinbase(); }, diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 9a640e3..b353004 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -1039,7 +1039,13 @@ void I18n::loadBuiltinEnglish() strings_["confirm_transaction"] = "Confirm Transaction"; strings_["confirm_and_send"] = "Confirm & Send"; strings_["cancel"] = "Cancel"; - + + // Wallet-switch "stop the running node?" confirmation + strings_["switch_stopnode_title"] = "Stop the running node?"; + strings_["switch_stopnode_warn"] = "A node is already running that this wallet didn't start."; + strings_["switch_stopnode_body"] = "Switching wallets restarts the node on the wallet you selected. The running node will be stopped and relaunched on the new wallet — if you kept it running on purpose, it comes back up automatically."; + strings_["switch_stopnode_confirm"] = "Stop node & switch"; + // Receive Tab strings_["receiving_addresses"] = "Your Receiving Addresses"; strings_["new_z_shielded"] = "New z-Address (Shielded)";