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 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 13:22:00 -05:00
co-authored by Claude Opus 5.5
parent ae66b54694
commit a581f42a33
15 changed files with 115 additions and 4 deletions
+3
View File
@@ -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",
+3
View File
@@ -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",
+3
View File
@@ -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",
+3
View File
@@ -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 アドレスが有効かどうかを確認",
+3
View File
@@ -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 주소가 유효한지 확인",
+3
View File
@@ -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",
+3
View File
@@ -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",
+3
View File
@@ -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 地址是否有效",
+2
View File
@@ -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 }
+10
View File
@@ -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<rpc::RPCClient>();
+4
View File
@@ -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_; }
+16 -2
View File
@@ -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());
+17 -2
View File
@@ -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();
+3
View File
@@ -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";
+39
View File
@@ -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();