Integrate Tor V3 onion identity into wallet startup, fix freeze

The wallet was freezing on "Verifying database integrity" because the
old Tor v2 startup code blocked on a mutex (wait_initialized) and
required an onion/hostname file that no longer existed after v2 removal.

Replace the blocking v2 startup with internal V3 onion identity:
- Every wallet now auto-generates Ed25519 keys and a V3 .onion address
- Keys are stored in wallet.dat (encrypted if wallet is encrypted)
- Onion identity is created after wallet loads (Step 8.5) so keys
  persist across restarts
- No external Tor process required for identity generation
- Writes onion/hostname and tor_data/ config for optional external Tor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 12:35:51 -07:00
parent 958adea2b6
commit ae5e97ad6e
3 changed files with 73 additions and 53 deletions
+41 -26
View File
@@ -11,6 +11,7 @@
#include "ui_interface.h"
#include "checkpoints.h"
#include "smessage.h"
#include "tor/onion_v3.h"
#ifdef ENABLE_ZMQ
#include "zmqpublishnotifier.h"
#endif
@@ -152,6 +153,7 @@ void Shutdown(void* parg)
}
SecureMsgShutdown();
ShutdownTorV3();
#ifdef ENABLE_ZMQ
if (pzmqNotifier)
@@ -748,14 +750,8 @@ bool AppInit2()
}
// start up tor
if (!(mapArgs.count("-tor") && mapArgs["-tor"] != "0")) {
if (!NewThread(StartTor, NULL))
InitError(_("Error: could not start tor"));
}
wait_initialized();
// Release the old Tor initialization mutex (no longer blocking on embedded Tor)
set_initialized();
if (mapArgs.count("-externalip"))
{
@@ -765,25 +761,8 @@ bool AppInit2()
return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str()));
AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
}
} else {
string automatic_onion;
fs::path const hostname_path = GetDataDir(
) / "onion" / "hostname";
if (
!fs::exists(
hostname_path
)
) {
return InitError(strprintf(_("No external address found. %s"), hostname_path.string().c_str()));
}
ifstream file(
hostname_path.string(
).c_str(
)
);
file >> automatic_onion;
AddLocal(CService(automatic_onion, GetListenPort(), fNameLookup), LOCAL_MANUAL);
}
// Tor V3 onion address is registered after wallet loads (Step 8.5)
if (mapArgs.count("-reservebalance")) // triangles: reserve balance amount
{
@@ -977,6 +956,42 @@ bool AppInit2()
printf(" rescan %15"PRId64"ms\n", GetTimeMillis() - nStart);
}
// ********************************************************* Step 8.5: initialize Tor V3 identity
{
uiInterface.InitMessage(_("Initializing Tor V3 identity..."));
printf("Initializing Tor V3 onion identity...\n");
// Tor V3 identity is innate to Triangles — always enabled
LoadTorV3Config();
TorV3Config& torConfig = GetTorV3Config();
torConfig.enableTor = true;
torConfig.enableHiddenService = true;
torConfig.hiddenServicePort = GetListenPort();
torConfig.torDataDirectory = (GetDataDir() / "tor_data").string();
if (InitTorV3()) {
string onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
if (!onionAddr.empty()) {
// Write onion/hostname for compatibility with existing code paths
fs::path onionDir = GetDataDir() / "onion";
fs::create_directories(onionDir);
ofstream hostnameFile((onionDir / "hostname").string().c_str());
if (hostnameFile.is_open()) {
hostnameFile << onionAddr << endl;
hostnameFile.close();
}
// Register onion address as local address for peer discovery
AddLocal(CService(onionAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
printf("Tor V3 identity: %s\n", onionAddr.c_str());
} else {
printf("WARNING: Tor V3 initialized but no onion address available\n");
}
} else {
printf("WARNING: Failed to initialize Tor V3 identity\n");
}
}
// ********************************************************* Step 9: import blocks
if (mapArgs.count("-loadblock"))
+28 -23
View File
@@ -507,32 +507,34 @@ bool CTorV3Service::StartOnionService()
printf("ERROR: No onion address generated\n");
return false;
}
// Use data directory for Tor service files
boost::filesystem::path serviceDir = GetDataDir() / "tor_data" / "triangles_v3";
boost::filesystem::create_directories(serviceDir);
// Create Tor configuration for hidden service
std::string torrcContent = strprintf(
"HiddenServiceDir tor_data/triangles_v3/\n"
"HiddenServiceDir %s\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort %d 127.0.0.1:%d\n",
port, port
serviceDir.string().c_str(), port, port
);
// Write torrc file
boost::filesystem::create_directories("tor_data/triangles_v3");
std::ofstream torrcFile("tor_data/triangles_v3/torrc");
std::ofstream torrcFile((serviceDir / "torrc").string().c_str());
if (torrcFile.is_open()) {
torrcFile << torrcContent;
torrcFile.close();
}
// Write private key
std::ofstream keyFile("tor_data/triangles_v3/hs_ed25519_secret_key");
std::ofstream keyFile((serviceDir / "hs_ed25519_secret_key").string().c_str());
if (keyFile.is_open()) {
keyFile << "== ed25519v1-secret: type0 ==\n";
keyFile << privateKey << "\n";
keyFile.close();
}
isActive = true;
printf("Started V3 onion service on %s:%d\n", onionAddress.c_str(), port);
return true;
@@ -1097,7 +1099,7 @@ CTorV3Manager* CTorV3Manager::GetInstance()
CTorV3Manager::CTorV3Manager() : torEnabled(false), torDataDir("tor_data")
{
LoadTorV3Config();
// Config is loaded via LoadTorV3Config() before InitTorV3() is called
}
CTorV3Manager::~CTorV3Manager()
@@ -1108,16 +1110,19 @@ CTorV3Manager::~CTorV3Manager()
bool CTorV3Manager::InitializeTor()
{
printf("Initializing Tor V3 support...\n");
// Use configured data directory (set by init.cpp to GetDataDir()/tor_data)
torDataDir = torV3Config.torDataDirectory;
// Create tor data directory
boost::filesystem::create_directories(torDataDir);
torEnabled = true;
if (torV3Config.enableHiddenService) {
return CreateWalletHiddenService(torV3Config.hiddenServicePort);
}
return true;
}
@@ -1962,21 +1967,21 @@ void CTorV3Manager::UpdateDiscoveryStats(int connected, int attempted)
// Load Tor V3 configuration
bool LoadTorV3Config()
{
// Set default values
torV3Config.enableTor = GetBoolArg("-tor", false);
torV3Config.enableHiddenService = GetBoolArg("-torhiddenservice", false);
// Tor V3 identity is innate to Triangles — enabled by default
torV3Config.enableTor = GetBoolArg("-tor", true);
torV3Config.enableHiddenService = GetBoolArg("-torhiddenservice", true);
torV3Config.enableSeederMode = GetBoolArg("-torseeder", false);
torV3Config.hiddenServicePort = GetArg("-torhiddenserviceport", 19112);
torV3Config.torDataDirectory = GetArg("-tordatadir", "tor_data");
torV3Config.hiddenServicePort = GetArg("-torhiddenserviceport", GetDefaultPort());
torV3Config.torDataDirectory = GetArg("-tordatadir", (GetDataDir() / "tor_data").string());
torV3Config.socksProxy = GetArg("-torproxy", "127.0.0.1:9050");
torV3Config.maxConnections = GetArg("-tormaxconnections", 8);
printf("Loaded Tor V3 configuration: enabled=%s, hidden_service=%s, seeder=%s, proxy=%s\n",
torV3Config.enableTor ? "true" : "false",
torV3Config.enableHiddenService ? "true" : "false",
torV3Config.enableHiddenService ? "true" : "false",
torV3Config.enableSeederMode ? "true" : "false",
torV3Config.socksProxy.c_str());
return true;
}
+4 -4
View File
@@ -149,11 +149,11 @@ struct TorV3Config
int maxConnections;
std::vector<std::string> trustedOnionPeers;
TorV3Config() :
enableTor(false),
enableHiddenService(false),
TorV3Config() :
enableTor(true),
enableHiddenService(true),
enableSeederMode(false),
hiddenServicePort(19112),
hiddenServicePort(24112),
torDataDirectory("tor_data"),
socksProxy("127.0.0.1:9050"),
maxConnections(8) {}