Tor v3: fix hidden service backend, add key persistence and health monitoring

Codex changes: delegate hidden service management to the actual Tor
backend instead of generating keys the wallet never served. The new
AttachToBackendService() reads the hostname Tor creates, and the
torrc/process plumbing properly gates HiddenService directives behind
the -torhiddenservice flag.

Additional fixes:
- Back up hs_ed25519_secret_key (96 bytes) to wallet.dat so the onion
  identity survives deletion of tor_data/
- Restore the key before Tor starts so the same .onion address is
  regenerated automatically
- Add ThreadTorMaintenance: checks Tor health every 30s, auto-restarts
  with exponential backoff on crash, re-attaches the hidden service
  and re-registers the onion address with AddLocal()
- Seeder maintenance: every 30 min re-announces to peers and refreshes
  known seeder lists (when -torseeder is enabled)
- Clean up ScheduleSeederReannouncement() stub (real work now in thread)
- Respect -torsocks port in onion proxy registration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 00:27:22 -07:00
parent 9a3b643a5a
commit 8cab86518c
7 changed files with 355 additions and 99 deletions
+35 -23
View File
@@ -52,6 +52,7 @@ CTorEmbedded::CTorEmbedded()
: running(false)
, socksPort(19099)
, hiddenServicePort(24112)
, hiddenServiceEnabled(true)
{
}
@@ -109,20 +110,24 @@ static void TorThreadFunc(std::vector<std::string> argv_strings)
CTorEmbedded::GetInstance()->SetRunning(false);
}
bool CTorEmbedded::Start(int socks, int hsPort)
bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
{
if (running.load()) return true;
socksPort = socks;
hiddenServicePort = hsPort;
hiddenServiceEnabled = enableHiddenService;
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
onionHostname.clear();
// Prepare Tor data directory under the wallet's data dir
torDataDir = (::GetDataDir() / "tor_data").string();
fs::create_directories(torDataDir);
// Hidden service directory
std::string hsDir = (fs::path(torDataDir) / "hidden_service").string();
fs::create_directories(hsDir);
std::string hsDir;
if (hiddenServiceEnabled) {
hsDir = (fs::path(torDataDir) / "hidden_service").string();
fs::create_directories(hsDir);
}
// Build the argv for tor_run_main
std::vector<std::string> argv;
@@ -131,12 +136,14 @@ bool CTorEmbedded::Start(int socks, int hsPort)
argv.push_back(std::to_string(socksPort));
argv.push_back("--DataDirectory");
argv.push_back(torDataDir);
argv.push_back("--HiddenServiceDir");
argv.push_back(hsDir);
argv.push_back("--HiddenServiceVersion");
argv.push_back("3");
argv.push_back("--HiddenServicePort");
argv.push_back(std::to_string(hiddenServicePort) + " 127.0.0.1:" + std::to_string(hiddenServicePort));
if (hiddenServiceEnabled) {
argv.push_back("--HiddenServiceDir");
argv.push_back(hsDir);
argv.push_back("--HiddenServiceVersion");
argv.push_back("3");
argv.push_back("--HiddenServicePort");
argv.push_back(std::to_string(hiddenServicePort) + " 127.0.0.1:" + std::to_string(hiddenServicePort));
}
argv.push_back("--AvoidDiskWrites");
argv.push_back("1");
argv.push_back("--Log");
@@ -175,13 +182,15 @@ bool CTorEmbedded::Start(int socks, int hsPort)
printf("Embedded Tor SOCKS proxy ready on port %d (took %ds)\n", socksPort, i + 1);
// Read .onion hostname if available
fs::path hostnameFile = fs::path(hsDir) / "hostname";
if (fs::exists(hostnameFile)) {
std::ifstream f(hostnameFile.string().c_str());
if (f.is_open())
std::getline(f, onionHostname);
if (!onionHostname.empty())
printf("Tor hidden service: %s\n", onionHostname.c_str());
if (hiddenServiceEnabled) {
fs::path hostnameFile = fs::path(hsDir) / "hostname";
if (fs::exists(hostnameFile)) {
std::ifstream f(hostnameFile.string().c_str());
if (f.is_open())
std::getline(f, onionHostname);
if (!onionHostname.empty())
printf("Tor hidden service: %s\n", onionHostname.c_str());
}
}
return true;
}
@@ -219,14 +228,16 @@ void CTorEmbedded::Stop()
#include "tor_process.h"
bool CTorEmbedded::Start(int socks, int hsPort)
bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
{
printf("Embedded Tor not compiled in. Using external Tor process.\n");
// Delegate to the external process manager
socksPort = socks;
hiddenServicePort = hsPort;
hiddenServiceEnabled = enableHiddenService;
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
onionHostname.clear();
torDataDir = (::GetDataDir() / "tor_data").string();
running.store(StartTorProcess(torDataDir, socksPort, hiddenServicePort));
running.store(StartTorProcess(torDataDir, socksPort, hiddenServicePort, hiddenServiceEnabled));
return running.load();
}
@@ -249,10 +260,11 @@ bool StartEmbeddedTor()
return false;
}
bool enableHiddenService = GetBoolArg("-torhiddenservice", true);
int socksPort = GetArg("-torsocks", 19099);
int hsPort = GetArg("-torhsport", GetListenPort());
int hsPort = enableHiddenService ? GetArg("-torhsport", GetListenPort()) : 0;
return CTorEmbedded::GetInstance()->Start(socksPort, hsPort);
return CTorEmbedded::GetInstance()->Start(socksPort, hsPort, enableHiddenService);
}
void StopEmbeddedTor()