From a581f42a331813b2ae28df0fd62a41c62a3b3051 Mon Sep 17 00:00:00 2001 From: DanS Date: Mon, 12 Oct 2026 13:22:00 -0500 Subject: [PATCH] fix(settings): disable the unimplemented Tor option and tell its users The "Use Tor" checkbox was saved but never read: no build routed any traffic (node, RPC, price/update/bootstrap/pool HTTP or Lite gRPC) through Tor, so users who ticked it believed they were anonymous. - The checkbox is disabled and always shown unticked, on the Settings page and in the legacy Settings window. Its tooltip says it is not available and gives the alternatives: for the node, proxy=127.0.0.1:9050 in DRAGONX.conf with Tor running; otherwise a system-wide VPN/Tor (Lite builds get the short version). The "Use Tor" label is kept. - Behaviour change: a stored use_tor=true is cleared once at startup (Settings::consumeLegacyUseTor + a synchronous save before the async writer and any Settings page state), with a 20 s warning in the user's language. The cleared key is the "already told" flag, so the notice never repeats. No settings format change. - 3 new strings (tt_tor_unavailable, tt_tor_unavailable_lite, tor_never_applied_notice) in all 8 languages; the tooltip wraps at tor-tooltip-wrap-width from ui.toml. Co-Authored-By: Claude Opus 5.5 --- res/lang/de.json | 3 +++ res/lang/es.json | 3 +++ res/lang/fr.json | 3 +++ res/lang/ja.json | 3 +++ res/lang/ko.json | 3 +++ res/lang/pt.json | 3 +++ res/lang/ru.json | 3 +++ res/lang/zh.json | 3 +++ res/themes/ui.toml | 2 ++ src/app.cpp | 10 ++++++++ src/config/settings.h | 4 +++ src/ui/pages/settings_page.cpp | 18 ++++++++++++-- src/ui/windows/settings_window.cpp | 19 +++++++++++++-- src/util/i18n.cpp | 3 +++ tests/test_phase4.cpp | 39 ++++++++++++++++++++++++++++++ 15 files changed, 115 insertions(+), 4 deletions(-) diff --git a/res/lang/de.json b/res/lang/de.json index 41ad59d..76f21e0 100644 --- a/res/lang/de.json +++ b/res/lang/de.json @@ -2019,6 +2019,7 @@ "tools": "WERKZEUGE", "tools_actions": "Werkzeuge & Aktionen...", "tools_actions_hdr": "WERKZEUGE & AKTIONEN", + "tor_never_applied_notice": "Die Option „Tor verwenden“ hat nie funktioniert: Ihre Verbindungen wurden NICHT über Tor geleitet. Sie wurde ausgeschaltet. Alternativen finden Sie im Tooltip der Option in den Einstellungen.", "total": "Gesamt", "total_balance_label": "Gesamtguthaben", "transaction_id": "TRANSAKTIONS-ID", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "Schimmer, Glühen, Farbton-Zyklen pro Theme", "tt_theme_hotkey": "Hotkey: Ctrl+Links/Rechts zum Wechseln der Themes", "tt_tor": "Daemon-Verbindungen für Anonymität über das Tor-Netzwerk leiten", + "tt_tor_unavailable": "Noch nicht verfügbar: Diese Wallet leitet keinen Datenverkehr über Tor. Nur für den Node können Sie proxy=127.0.0.1:9050 zu DRAGONX.conf hinzufügen und den Node neu starten. Tor muss laufen, sonst findet der Node keine Peers. Preis-, Update-, Bootstrap- und Pool-Anfragen verbinden sich weiterhin direkt; nutzen Sie dafür ein systemweites VPN/Tor.", + "tt_tor_unavailable_lite": "Noch nicht verfügbar: Diese Wallet leitet keinen Datenverkehr über Tor. Nutzen Sie ein systemweites VPN/Tor.", "tt_tx_url": "Basis-URL zum Anzeigen von Transaktionen in einem Block-Explorer", "tt_ui_opacity": "Karten- und Seitenleisten-Deckkraft (100% = vollständig undurchsichtig, niedriger = durchsichtiger)", "tt_validate": "Prüfen, ob eine DragonX-Adresse gültig ist", diff --git a/res/lang/es.json b/res/lang/es.json index b9c016d..25a11ed 100644 --- a/res/lang/es.json +++ b/res/lang/es.json @@ -2019,6 +2019,7 @@ "tools": "HERRAMIENTAS", "tools_actions": "Herramientas y Acciones...", "tools_actions_hdr": "HERRAMIENTAS Y ACCIONES", + "tor_never_applied_notice": "La opción \"Usar Tor\" nunca funcionó: tus conexiones NO se enrutaron a través de Tor. Se ha desactivado. Consulta su descripción emergente en Ajustes para ver alternativas.", "total": "Total", "total_balance_label": "Saldo Total", "transaction_id": "ID DE TRANSACCIÓN", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "Brillo, resplandor, ciclo de tono por tema", "tt_theme_hotkey": "Atajo: Ctrl+Izquierda/Derecha para cambiar temas", "tt_tor": "Enrutar conexiones del daemon a través de la red Tor para anonimato", + "tt_tor_unavailable": "Aún no disponible: esta cartera no enruta ningún tráfico a través de Tor. Solo para el nodo, puedes añadir proxy=127.0.0.1:9050 a DRAGONX.conf y reiniciar el nodo. Tor debe estar en ejecución o el nodo no encontrará otros nodos. Las solicitudes de precios, actualizaciones, bootstrap y pool siguen conectándose directamente; para ellas usa una VPN/Tor de todo el sistema.", + "tt_tor_unavailable_lite": "Aún no disponible: esta cartera no enruta ningún tráfico a través de Tor. Usa una VPN/Tor de todo el sistema.", "tt_tx_url": "URL base para ver transacciones en un explorador de bloques", "tt_ui_opacity": "Opacidad de tarjetas y barra lateral (100% = totalmente opaco, menor = más transparente)", "tt_validate": "Comprobar si una dirección DragonX es válida", diff --git a/res/lang/fr.json b/res/lang/fr.json index ea27d98..33ab00a 100644 --- a/res/lang/fr.json +++ b/res/lang/fr.json @@ -2019,6 +2019,7 @@ "tools": "OUTILS", "tools_actions": "Outils & Actions...", "tools_actions_hdr": "OUTILS ET ACTIONS", + "tor_never_applied_notice": "L'option « Utiliser Tor » n'a jamais fonctionné : vos connexions n'ont PAS été acheminées via Tor. Elle a été désactivée. Consultez son info-bulle dans Paramètres pour des alternatives.", "total": "Total", "total_balance_label": "Solde total", "transaction_id": "ID DE TRANSACTION", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "Scintillement, lueur, cycle de teinte par thème", "tt_theme_hotkey": "Raccourci : Ctrl+Gauche/Droite pour changer de thème", "tt_tor": "Acheminer les connexions du daemon via le réseau Tor pour l'anonymat", + "tt_tor_unavailable": "Pas encore disponible : ce portefeuille n'achemine aucun trafic via Tor. Pour le nœud uniquement, vous pouvez ajouter proxy=127.0.0.1:9050 à DRAGONX.conf et redémarrer le nœud. Tor doit être en cours d'exécution, sinon le nœud ne trouve aucun pair. Les requêtes de prix, de mise à jour, de bootstrap et de pool se connectent toujours directement ; pour celles-ci, utilisez un VPN/Tor à l'échelle du système.", + "tt_tor_unavailable_lite": "Pas encore disponible : ce portefeuille n'achemine aucun trafic via Tor. Utilisez un VPN/Tor à l'échelle du système.", "tt_tx_url": "URL de base pour consulter les transactions dans un explorateur de blocs", "tt_ui_opacity": "Opacité des cartes et de la barre latérale (100% = entièrement opaque, plus bas = plus transparent)", "tt_validate": "Vérifier si une adresse DragonX est valide", diff --git a/res/lang/ja.json b/res/lang/ja.json index b885905..2398feb 100644 --- a/res/lang/ja.json +++ b/res/lang/ja.json @@ -2019,6 +2019,7 @@ "tools": "ツール", "tools_actions": "ツールとアクション...", "tools_actions_hdr": "ツールと操作", + "tor_never_applied_notice": "「Torを使用」オプションは一度も機能していませんでした:接続は Tor 経由でルーティングされていませんでした。このオプションはオフにしました。代替手段については、設定にあるこのオプションのツールチップをご覧ください。", "total": "合計", "total_balance_label": "総残高", "transaction_id": "取引ID", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "テーマごとのシマー、グロー、色相サイクル", "tt_theme_hotkey": "ホットキー:Ctrl+左/右でテーマを切り替え", "tt_tor": "匿名性のためにデーモン接続を Tor ネットワーク経由でルーティング", + "tt_tor_unavailable": "まだ利用できません:このウォレットはどの通信も Tor 経由でルーティングしません。ノードに限り、DRAGONX.conf に proxy=127.0.0.1:9050 を追加してノードを再起動できます。Tor が起動している必要があり、起動していないとノードはピアを見つけられません。価格、アップデート、ブートストラップ、プールのリクエストは引き続き直接接続されます。これらにはシステム全体の VPN/Tor を使用してください。", + "tt_tor_unavailable_lite": "まだ利用できません:このウォレットはどの通信も Tor 経由でルーティングしません。システム全体の VPN/Tor を使用してください。", "tt_tx_url": "ブロックエクスプローラーでトランザクションを表示するためのベース URL", "tt_ui_opacity": "カードとサイドバーの不透明度(100% = 完全不透明、低い = より透過)", "tt_validate": "DragonX アドレスが有効かどうかを確認", diff --git a/res/lang/ko.json b/res/lang/ko.json index a6bea76..b3cea3e 100644 --- a/res/lang/ko.json +++ b/res/lang/ko.json @@ -2019,6 +2019,7 @@ "tools": "도구", "tools_actions": "도구 및 작업...", "tools_actions_hdr": "도구 및 작업", + "tor_never_applied_notice": "\"Tor 사용\" 옵션은 한 번도 작동하지 않았습니다. 연결이 Tor를 통해 라우팅되지 않았습니다. 이 옵션은 꺼졌습니다. 대안은 설정에서 이 옵션의 설명을 참고하세요.", "total": "합계", "total_balance_label": "총 잔액", "transaction_id": "거래 ID", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "테마별 반짝임, 글로우, 색조 순환", "tt_theme_hotkey": "단축키: Ctrl+왼쪽/오른쪽으로 테마 전환", "tt_tor": "익명성을 위해 데몬 연결을 Tor 네트워크를 통해 라우팅", + "tt_tor_unavailable": "아직 사용할 수 없음: 이 지갑은 어떤 통신도 Tor를 통해 라우팅하지 않습니다. 노드에 한해 DRAGONX.conf에 proxy=127.0.0.1:9050을 추가하고 노드를 재시작할 수 있습니다. Tor가 실행 중이어야 하며, 그렇지 않으면 노드가 피어를 찾지 못합니다. 가격, 업데이트, 부트스트랩, 풀 요청은 계속 직접 연결됩니다. 이러한 요청에는 시스템 전체 VPN/Tor를 사용하세요.", + "tt_tor_unavailable_lite": "아직 사용할 수 없음: 이 지갑은 어떤 통신도 Tor를 통해 라우팅하지 않습니다. 시스템 전체 VPN/Tor를 사용하세요.", "tt_tx_url": "블록 탐색기에서 거래를 보기 위한 기본 URL", "tt_ui_opacity": "카드 및 사이드바 불투명도 (100% = 완전 불투명, 낮을수록 더 투명)", "tt_validate": "DragonX 주소가 유효한지 확인", diff --git a/res/lang/pt.json b/res/lang/pt.json index d471638..60fcc77 100644 --- a/res/lang/pt.json +++ b/res/lang/pt.json @@ -2019,6 +2019,7 @@ "tools": "FERRAMENTAS", "tools_actions": "Ferramentas e Ações...", "tools_actions_hdr": "FERRAMENTAS E AÇÕES", + "tor_never_applied_notice": "A opção \"Usar Tor\" nunca funcionou: suas conexões NÃO foram roteadas pelo Tor. Ela foi desativada. Veja a dica dela em Ajustes para alternativas.", "total": "Total", "total_balance_label": "Saldo Total", "transaction_id": "ID DA TRANSAÇÃO", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "Brilho, luminescência, ciclo de matiz por tema", "tt_theme_hotkey": "Atalho: Ctrl+Esquerda/Direita para alternar temas", "tt_tor": "Rotear conexões do daemon através da rede Tor para anonimato", + "tt_tor_unavailable": "Ainda não disponível: esta carteira não encaminha nenhum tráfego pelo Tor. Apenas para o nó, você pode adicionar proxy=127.0.0.1:9050 ao DRAGONX.conf e reiniciar o nó. O Tor precisa estar em execução, ou o nó não encontrará pares. Solicitações de preço, atualização, bootstrap e pool continuam se conectando diretamente; para elas, use uma VPN/Tor em todo o sistema.", + "tt_tor_unavailable_lite": "Ainda não disponível: esta carteira não encaminha nenhum tráfego pelo Tor. Use uma VPN/Tor em todo o sistema.", "tt_tx_url": "URL base para visualizar transações em um explorador de blocos", "tt_ui_opacity": "Opacidade de cartões e barra lateral (100% = totalmente opaco, menor = mais transparente)", "tt_validate": "Verificar se um endereço DragonX é válido", diff --git a/res/lang/ru.json b/res/lang/ru.json index 4903d91..244ec2a 100644 --- a/res/lang/ru.json +++ b/res/lang/ru.json @@ -2019,6 +2019,7 @@ "tools": "УТИЛИТЫ", "tools_actions": "Инструменты и действия...", "tools_actions_hdr": "ИНСТРУМЕНТЫ И ДЕЙСТВИЯ", + "tor_never_applied_notice": "Опция «Использовать Tor» никогда не работала: ваши соединения НЕ направлялись через Tor. Она отключена. Альтернативы описаны в её подсказке в Настройках.", "total": "Итого", "total_balance_label": "Общий баланс", "transaction_id": "ID ТРАНЗАКЦИИ", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "Мерцание, свечение, циклическая смена оттенка по теме", "tt_theme_hotkey": "Горячая клавиша: Ctrl+Влево/Вправо для переключения тем", "tt_tor": "Маршрутизировать подключения демона через сеть Tor для анонимности", + "tt_tor_unavailable": "Пока недоступно: этот кошелёк не направляет никакой трафик через Tor. Только для узла можно добавить proxy=127.0.0.1:9050 в DRAGONX.conf и перезапустить узел. Tor должен быть запущен, иначе узел не найдёт других узлов. Запросы цен, обновлений, bootstrap и пула по-прежнему идут напрямую; для них используйте общесистемный VPN/Tor.", + "tt_tor_unavailable_lite": "Пока недоступно: этот кошелёк не направляет никакой трафик через Tor. Используйте общесистемный VPN/Tor.", "tt_tx_url": "Базовый URL для просмотра транзакций в обозревателе блоков", "tt_ui_opacity": "Непрозрачность карточек и боковой панели (100% = полностью непрозрачно, ниже = прозрачнее)", "tt_validate": "Проверить, действителен ли адрес DragonX", diff --git a/res/lang/zh.json b/res/lang/zh.json index 87f967e..0305b8c 100644 --- a/res/lang/zh.json +++ b/res/lang/zh.json @@ -2019,6 +2019,7 @@ "tools": "工具", "tools_actions": "工具与操作...", "tools_actions_hdr": "工具与操作", + "tor_never_applied_notice": "“使用 Tor”选项从未生效:您的连接并未通过 Tor 路由。该选项已关闭。其他方法请参阅设置中该选项的提示。", "total": "合计", "total_balance_label": "总余额", "transaction_id": "交易 ID", @@ -2146,6 +2147,8 @@ "tt_theme_effects": "每个主题的闪烁、发光、色调循环", "tt_theme_hotkey": "快捷键:Ctrl+左/右箭头切换主题", "tt_tor": "通过 Tor 网络路由守护进程连接以实现匿名", + "tt_tor_unavailable": "暂不可用:此钱包不会通过 Tor 路由任何流量。仅就节点而言,您可以在 DRAGONX.conf 中添加 proxy=127.0.0.1:9050 并重启节点。Tor 必须正在运行,否则节点将找不到其他节点。价格、更新、引导和矿池请求仍会直接连接;如需保护这些请求,请使用系统级 VPN/Tor。", + "tt_tor_unavailable_lite": "暂不可用:此钱包不会通过 Tor 路由任何流量。请使用系统级 VPN/Tor。", "tt_tx_url": "在区块浏览器中查看交易的基础 URL", "tt_ui_opacity": "卡片和侧边栏不透明度(100% = 完全不透明,越低越透明)", "tt_validate": "检查 DragonX 地址是否有效", diff --git a/res/themes/ui.toml b/res/themes/ui.toml index 931a729..f5b1ef1 100644 --- a/res/themes/ui.toml +++ b/res/themes/ui.toml @@ -1352,6 +1352,8 @@ rpc-label-width = { size = 85.0 } security-combo-width = { size = 120.0 } # Wrap width of the Security band's "PIN out of date" tooltip (a long sentence in every language). pin-tooltip-wrap-width = { size = 420.0 } +# Wrap width of the disabled "Use Tor" checkbox's tooltip (Settings page and the legacy Settings window). +tor-tooltip-wrap-width = { size = 420.0 } node-grid-breakpoint = { size = 900.0 } port-input-min-width = { size = 60.0 } port-input-width-ratio = { size = 0.4 } diff --git a/src/app.cpp b/src/app.cpp index 0e44767..f6c3b07 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -412,6 +412,14 @@ bool App::init() settings_->save(); // no writer attached yet: synchronous settings_->clearUpgradeSave(); } + // "Use Tor" never did anything. Clear a stored opt-in once, synchronously and before any Settings + // page state is loaded (so nothing writes it back), and warn that user below, once the saved + // language is loaded. + const bool legacyTorCleared = settings_->consumeLegacyUseTor(); + if (legacyTorCleared) { + DEBUG_LOGF("[INFO] Cleared the unimplemented use_tor setting\n"); + settings_->save(); // no writer attached yet: synchronous + } // From here on settings_->save() is written by file_writer_ off the UI thread (flushed by main() // at exit); a save after a fund/security-critical setter still waits for the disk (Settings::save). settings_->setFileWriter(&file_writer_); @@ -501,6 +509,8 @@ bool App::init() if (!lang.empty()) { util::I18n::instance().loadLanguage(lang); } + if (legacyTorCleared) + ui::Notifications::instance().warning(TR("tor_never_applied_notice"), 20.0f); // Initialize RPC client rpc_ = std::make_unique(); diff --git a/src/config/settings.h b/src/config/settings.h index 815a899..41f8f11 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -201,6 +201,10 @@ public: bool getUseTor() const { return use_tor_; } void setUseTor(bool tor) { use_tor_ = tor; } + // "Use Tor" was never implemented: no build routed any traffic through Tor. Clears a stored opt-in + // and reports whether there was one, so App::init can tell that user once (the cleared key is the + // "already told" flag). + bool consumeLegacyUseTor() { const bool was = use_tor_; use_tor_ = false; return was; } // Fees bool getAllowCustomFees() const { return allow_custom_fees_; } diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index a25cbe5..fd20f91 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -1250,8 +1250,22 @@ void RenderSettingsPage(App* app) { if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_save_ztx")); CB(TrId("auto_shield", "auto_shld"), &s_settingsState.auto_shield); if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_auto_shield")); - CB(TrId("use_tor", "tor"), &s_settingsState.use_tor); - if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_tor")); + { + // Not implemented: disabled and never shown as "on", even for a stored use_tor=true. + bool torOff = false; + ImGui::BeginDisabled(); + CB(TrId("use_tor", "tor"), &torOff); + ImGui::EndDisabled(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + const float wrapW = S.drawElement("components.settings-page", "tor-tooltip-wrap-width") + .sizeOr(420.0f) * Layout::dpiScale(); + material::BeginTooltip(); + ImGui::PushTextWrapPos(wrapW); + ImGui::TextUnformatted(TR(app->isLiteBuild() ? "tt_tor_unavailable_lite" : "tt_tor_unavailable")); + ImGui::PopTextWrapPos(); + material::EndTooltip(); + } + } if (showDaemonOptions) { if (CB(TrId("keep_daemon", "keep_dmn"), &s_settingsState.keep_daemon_running)) saveSettingsPageState(app->settings()); diff --git a/src/ui/windows/settings_window.cpp b/src/ui/windows/settings_window.cpp index 21c255c..d42a3c8 100644 --- a/src/ui/windows/settings_window.cpp +++ b/src/ui/windows/settings_window.cpp @@ -369,8 +369,23 @@ void RenderSettingsWindow(App* app, bool* p_open) ImGui::Spacing(); - ImGui::Checkbox(TrId("settings_use_tor_network", "tor_w").c_str(), &s_use_tor); - ImGui::TextDisabled(" %s", TR("settings_tor_desc")); + { + // Not implemented: disabled and never shown as "on", even for a stored use_tor=true. + bool torOff = false; + ImGui::BeginDisabled(); + ImGui::Checkbox(TrId("settings_use_tor_network", "tor_w").c_str(), &torOff); + ImGui::EndDisabled(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + const float wrapW = S.drawElement("components.settings-page", "tor-tooltip-wrap-width") + .sizeOr(420.0f) * Layout::dpiScale(); + material::BeginTooltip(); + ImGui::PushTextWrapPos(wrapW); + ImGui::TextUnformatted(TR(app && app->isLiteBuild() ? "tt_tor_unavailable_lite" + : "tt_tor_unavailable")); + ImGui::PopTextWrapPos(); + material::EndTooltip(); + } + } ImGui::Spacing(); ImGui::Separator(); diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 9c318bb..f160902 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -806,6 +806,9 @@ void I18n::loadBuiltinEnglish() strings_["tt_save_ztx"] = "Store z-address transaction history locally for faster loading"; strings_["tt_auto_shield"] = "Automatically move transparent balance to shielded addresses for privacy"; strings_["tt_tor"] = "Route daemon connections through the Tor network for anonymity"; + strings_["tt_tor_unavailable"] = "Not available yet: this wallet does not route any traffic through Tor. For the node only, you can add proxy=127.0.0.1:9050 to DRAGONX.conf and restart the node. Tor must be running, or the node finds no peers. Price, update, bootstrap and pool requests still connect directly; use a system-wide VPN/Tor for those."; + strings_["tt_tor_unavailable_lite"] = "Not available yet: this wallet does not route any traffic through Tor. Use a system-wide VPN/Tor."; + strings_["tor_never_applied_notice"] = "The \"Use Tor\" option was never functional: your connections were NOT routed through Tor. It has been switched off. See its tooltip in Settings for alternatives."; strings_["tt_keep_daemon"] = "Daemon will still stop when running the setup wizard"; strings_["tt_stop_external"] = "Applies when connecting to a daemon\nyou started outside this wallet"; strings_["stratum_host_section"] = "MINING POOL HOSTING"; diff --git a/tests/test_phase4.cpp b/tests/test_phase4.cpp index 2d8588d..f6c8666 100644 --- a/tests/test_phase4.cpp +++ b/tests/test_phase4.cpp @@ -13541,6 +13541,44 @@ void testSweepSettingsLatch() #endif } +// C94: "Use Tor" was never implemented. A stored use_tor=true is consumed exactly once (App::init warns +// that user) and stays false after a save and reload, so the notice is never shown again. +void testLegacyUseTorClearedOnce() +{ +#ifdef _WIN32 + std::printf("[skip] testLegacyUseTorClearedOnce (Windows config dir can't be redirected)\n"); +#else + using dragonx::config::Settings; + withSweepTempHome([] { + const fs::path file = Settings::getDefaultPath(); + fs::create_directories(file.parent_path()); + writeTestFile(file, "{\n \"use_tor\": true,\n \"wizard_completed\": true\n}\n"); + { + Settings s; // no writer attached: synchronous saves + EXPECT_TRUE(s.load()); + EXPECT_TRUE(s.getUseTor()); // the legacy opt-in is read as stored + EXPECT_TRUE(s.consumeLegacyUseTor()); // first call: there was one + EXPECT_TRUE(!s.getUseTor()); + EXPECT_TRUE(!s.consumeLegacyUseTor()); // second call: nothing left to report + EXPECT_TRUE(s.save()); + } + { + Settings back; + EXPECT_TRUE(back.load()); + EXPECT_TRUE(!back.getUseTor()); + EXPECT_TRUE(back.getWizardCompleted()); // the rest of the file survives + EXPECT_TRUE(!back.consumeLegacyUseTor()); // a later start shows no notice + } + const std::string text = sweepReadBytes(file); + EXPECT_TRUE(text.find("\"use_tor\": false") != std::string::npos); + + // A profile that never ticked it reports nothing. + Settings fresh; + EXPECT_TRUE(!fresh.consumeLegacyUseTor()); + }); +#endif +} + // The sandbox: a fresh random owner-only dir; a pre-existing path or a symlink (even dangling) is refused, // and the remover only touches a path that still carries the prefix. void testSweepSandboxDir() @@ -17054,6 +17092,7 @@ int main() testSweepAddressBookLatch(); testSweepWalletIndexLatch(); testSweepSettingsLatch(); + testLegacyUseTorClearedOnce(); testSweepSandboxDir(); testTransactionHistoryCacheRefreshApply(); testLiteBridgeOwnedStringCopiesBeforeFreeOnSuccess();