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
+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();