Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16611efe72 | |||
| 1f3deacb7a | |||
| 8847571193 | |||
| a58eb3e9ef | |||
| dfb4b221dd | |||
| b6013edbe4 | |||
| d42c5aa799 | |||
| e8b2339339 | |||
| d242c2f37e |
Regular → Executable
+7
-2
@@ -15,8 +15,13 @@ if [ -e "$(which git)" ]; then
|
||||
# clean 'dirty' status of touched files that haven't been modified
|
||||
git diff >/dev/null 2>/dev/null
|
||||
|
||||
# get a string like "v0.6.0-66-g59887e8-dirty"
|
||||
DESC="$(git describe --dirty 2>/dev/null)"
|
||||
# Try exact tag match first (when building from a release tag)
|
||||
DESC="$(git describe --tags --exact-match 2>/dev/null)"
|
||||
|
||||
# If no exact match, fall back to git describe with commit distance
|
||||
if [ -z "$DESC" ]; then
|
||||
DESC="$(git describe --tags --dirty 2>/dev/null)"
|
||||
fi
|
||||
|
||||
# get a string like "2012-04-10 16:27:19 +0200"
|
||||
TIME="$(git log -n 1 --format="%ci")"
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
|
||||
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 5
|
||||
#define CLIENT_VERSION_MINOR 3
|
||||
#define CLIENT_VERSION_REVISION 9
|
||||
#define CLIENT_VERSION_MINOR 4
|
||||
#define CLIENT_VERSION_REVISION 2
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
+59
-10
@@ -176,6 +176,12 @@ void Shutdown(void* parg)
|
||||
}
|
||||
|
||||
SecureMsgShutdown();
|
||||
|
||||
// Stop network threads FIRST so nothing references Tor objects
|
||||
nTransactionsUpdated++;
|
||||
StopNode();
|
||||
|
||||
// NOW safe to destroy Tor state - all threads have stopped
|
||||
ShutdownTorV3();
|
||||
StopEmbeddedTor();
|
||||
|
||||
@@ -194,10 +200,8 @@ void Shutdown(void* parg)
|
||||
pNotificationQueue = NULL;
|
||||
}
|
||||
|
||||
nTransactionsUpdated++;
|
||||
// CTxDB().Close();
|
||||
bitdb.Flush(false);
|
||||
StopNode();
|
||||
bitdb.Flush(true);
|
||||
fs::remove(GetPidFile());
|
||||
UnregisterWallet(pwalletMain);
|
||||
@@ -355,6 +359,7 @@ std::string HelpMessage()
|
||||
" -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
|
||||
" -notor " + _("Disable Tor startup and .onion connectivity") + "\n" +
|
||||
" -torsocks=<port> " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\n" +
|
||||
" -torhiddenservice " + _("Enable the managed Tor hidden service (default: 1)") + "\n" +
|
||||
" -torhsport=<port> " + _("Set embedded or managed Tor hidden service port (default: wallet listen port)") + "\n" +
|
||||
//" -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" +
|
||||
" -port=<port> " + _("Listen for connections on <port> (default: 24112 or testnet: 24111)") + "\n" +
|
||||
@@ -752,7 +757,7 @@ bool AppInit2()
|
||||
|
||||
// Tor proxy: always configured for .onion connectivity
|
||||
CService addrOnion;
|
||||
unsigned short const onion_port = 19099;
|
||||
unsigned short const onion_port = static_cast<unsigned short>(GetArg("-torsocks", 19099));
|
||||
|
||||
if (mapArgs.count("-tor") && mapArgs["-tor"] != "0") {
|
||||
addrOnion = CService(mapArgs["-tor"], onion_port);
|
||||
@@ -1078,6 +1083,37 @@ bool AppInit2()
|
||||
uiInterface.InitMessage(_("Starting Tor..."));
|
||||
printf("Starting Tor process...\n");
|
||||
|
||||
// Restore hidden service secret key from wallet backup if the key
|
||||
// file is missing on disk. This preserves the .onion identity even
|
||||
// if the tor_data directory was deleted.
|
||||
if (pwalletMain && !GetBoolArg("-notor", false)) {
|
||||
std::string restoreDataPath = GetArg("-tordatadir", (GetDataDir() / "tor_data").string());
|
||||
fs::path secretKeyPath = fs::path(restoreDataPath) / "hidden_service" / "hs_ed25519_secret_key";
|
||||
|
||||
if (!fs::exists(secretKeyPath)) {
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile);
|
||||
std::vector<unsigned char> backedUpKey;
|
||||
|
||||
if (walletdb.ReadSetting("tor_v3_hs_secret_key_backup", backedUpKey) &&
|
||||
backedUpKey.size() == 96) {
|
||||
fs::create_directories(secretKeyPath.parent_path());
|
||||
|
||||
std::ofstream keyFile(secretKeyPath.string().c_str(), std::ios::binary);
|
||||
if (keyFile.is_open()) {
|
||||
keyFile.write(reinterpret_cast<const char*>(backedUpKey.data()),
|
||||
backedUpKey.size());
|
||||
keyFile.close();
|
||||
printf("Restored Tor hidden service secret key from wallet backup\n");
|
||||
} else {
|
||||
printf("WARNING: Failed to write restored hs_ed25519_secret_key to %s\n",
|
||||
secretKeyPath.string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
OPENSSL_cleanse(backedUpKey.data(), backedUpKey.size());
|
||||
}
|
||||
}
|
||||
|
||||
int64_t nTorStart = GetTimeMillis();
|
||||
bool torStarted = StartEmbeddedTor();
|
||||
StartupPerfLog("tor_start", GetTimeMillis() - nTorStart, strprintf("started=%d", torStarted));
|
||||
@@ -1100,13 +1136,14 @@ bool AppInit2()
|
||||
int64_t nTorIdentityStart = GetTimeMillis();
|
||||
LoadTorV3Config();
|
||||
TorV3Config& torConfig = GetTorV3Config();
|
||||
torConfig.enableTor = true;
|
||||
torConfig.enableHiddenService = true;
|
||||
torConfig.hiddenServicePort = GetListenPort();
|
||||
torConfig.enableTor = torStarted;
|
||||
torConfig.enableHiddenService = torStarted && CTorEmbedded::GetInstance()->IsHiddenServiceEnabled();
|
||||
torConfig.hiddenServicePort = CTorEmbedded::GetInstance()->GetHiddenServicePort();
|
||||
torConfig.torDataDirectory = torDataPath;
|
||||
std::string onionAddr;
|
||||
|
||||
if (InitTorV3()) {
|
||||
string onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
|
||||
if (torConfig.enableTor && torConfig.enableHiddenService && InitTorV3()) {
|
||||
onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
|
||||
if (!onionAddr.empty()) {
|
||||
// Write onion/hostname for compatibility with existing code paths
|
||||
fs::path onionDir = GetDataDir() / "onion";
|
||||
@@ -1118,11 +1155,15 @@ bool AppInit2()
|
||||
}
|
||||
|
||||
// Register onion address as local address for peer discovery
|
||||
AddLocal(CService(onionAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
|
||||
AddLocal(CService(onionAddr, torConfig.hiddenServicePort, 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 if (torStarted && !torConfig.enableHiddenService) {
|
||||
printf("Tor hidden service disabled by configuration\n");
|
||||
} else if (!torStarted) {
|
||||
printf("Skipping Tor V3 identity because the Tor backend is unavailable\n");
|
||||
} else {
|
||||
printf("WARNING: Failed to initialize Tor V3 identity\n");
|
||||
}
|
||||
@@ -1139,13 +1180,21 @@ bool AppInit2()
|
||||
while (!torOnion.empty() && (torOnion.back() == '\n' || torOnion.back() == '\r' || torOnion.back() == ' '))
|
||||
torOnion.pop_back();
|
||||
if (!torOnion.empty()) {
|
||||
AddLocal(CService(torOnion, GetListenPort(), fNameLookup), LOCAL_MANUAL);
|
||||
if (torOnion != onionAddr) {
|
||||
AddLocal(CService(torOnion, torConfig.hiddenServicePort, fNameLookup), LOCAL_MANUAL);
|
||||
}
|
||||
printf("Tor hidden service (from Tor process): %s\n", torOnion.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StartupPerfLog("tor_setup_total", GetTimeMillis() - nTorStart);
|
||||
|
||||
// Launch background thread for Tor health monitoring and seeder maintenance
|
||||
if (torStarted) {
|
||||
if (!NewThread(ThreadTorMaintenance, NULL))
|
||||
printf("Warning: ThreadTorMaintenance could not be started\n");
|
||||
}
|
||||
}
|
||||
|
||||
// ********************************************************* Step 9: import blocks
|
||||
|
||||
+13
-3
@@ -2568,7 +2568,17 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
|
||||
|
||||
// New best — keep the batch open so SetBestChain can add ConnectBlock
|
||||
// writes to the same transaction, cutting the per-block commit count in half.
|
||||
// v5.4: deterministic tiebreaker — when two chains have equal trust,
|
||||
// all nodes agree on the one whose tip has the lower block hash.
|
||||
// This prevents permanent forks from PoS blocks with identical difficulty.
|
||||
bool fNewBest = false;
|
||||
if (pindexNew->nChainTrust > nBestChainTrust)
|
||||
fNewBest = true;
|
||||
else if (pindexNew->nChainTrust == nBestChainTrust && pindexBest &&
|
||||
pindexNew->GetBlockHash() < pindexBest->GetBlockHash())
|
||||
fNewBest = true;
|
||||
|
||||
if (fNewBest)
|
||||
{
|
||||
if (!SetBestChain(txdb, pindexNew))
|
||||
return false;
|
||||
@@ -2719,7 +2729,7 @@ bool CBlock::AcceptBlock()
|
||||
if (nHeight % 10000 == 0 || nHeight > 2186900)
|
||||
printf("ProcessBlock(): Check proof-of-stake/work OK for block %d\n", nHeight);
|
||||
// Check timestamp against prev
|
||||
if (GetBlockTime() <= pindexPrev->GetPastTimeLimit() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
|
||||
if (GetBlockTime() <= pindexPrev->GetPastTimeLimit() || FutureDrift(GetBlockTime(), nHeight) < pindexPrev->GetBlockTime())
|
||||
return error("AcceptBlock() : block's timestamp is too early");
|
||||
|
||||
// Check that all transactions are finalized
|
||||
@@ -2999,13 +3009,13 @@ bool CBlock::SignBlock(CWallet& wallet, int64_t nFees)
|
||||
{
|
||||
if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, nFees, txCoinStake, key))
|
||||
{
|
||||
if (txCoinStake.nTime >= max(pindexBest->GetPastTimeLimit()+1, PastDrift(pindexBest->GetBlockTime())))
|
||||
if (txCoinStake.nTime >= max(pindexBest->GetPastTimeLimit()+1, PastDrift(pindexBest->GetBlockTime(), pindexBest->nHeight + 1)))
|
||||
{
|
||||
// make sure coinstake would meet timestamp protocol
|
||||
// as it would be the same as the block timestamp
|
||||
vtx[0].nTime = nTime = txCoinStake.nTime;
|
||||
nTime = max(pindexBest->GetPastTimeLimit()+1, GetMaxTransactionTime());
|
||||
nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime()));
|
||||
nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime(), pindexBest->nHeight + 1));
|
||||
|
||||
// we have to make sure that we have no future timestamps in
|
||||
// our transactions set
|
||||
|
||||
+6
-2
@@ -29,6 +29,7 @@ class CNode;
|
||||
static const int CUTOFF_POW_BLOCK = 9000;
|
||||
static const int CRAPCHAIN_CUTOFF_BLOCK = 17691; // pre-Pharao (version 4) blockchain until block 17691
|
||||
static const int FORK_HEIGHT_V5 = 17651; // v5 hard fork: decentralization + Tor v3 (next block after last checkpoint)
|
||||
static const int FORK_HEIGHT_V5_4 = 2186941; // v5.4: tighter timestamps, deterministic fork resolution
|
||||
|
||||
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
||||
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
|
||||
@@ -56,8 +57,11 @@ static const int fHaveUPnP = false;
|
||||
|
||||
static const uint256 hashGenesisBlockOfficial("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021");
|
||||
static const uint256 hashGenesisBlockTestNet ("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021");
|
||||
inline int64_t PastDrift(int64_t nTime) { return nTime - 10 * 60; } // up to 10 minutes from the past
|
||||
inline int64_t FutureDrift(int64_t nTime) { return nTime + 10 * 60; } // up to 10 minutes from the future
|
||||
inline int64_t GetMaxTimeDrift(int nHeight) { return (nHeight >= FORK_HEIGHT_V5_4) ? 3 * 60 : 10 * 60; }
|
||||
inline int64_t PastDrift(int64_t nTime, int nHeight) { return nTime - GetMaxTimeDrift(nHeight); }
|
||||
inline int64_t FutureDrift(int64_t nTime, int nHeight) { return nTime + GetMaxTimeDrift(nHeight); }
|
||||
inline int64_t PastDrift(int64_t nTime) { return PastDrift(nTime, nBestHeight); }
|
||||
inline int64_t FutureDrift(int64_t nTime) { return FutureDrift(nTime, nBestHeight); }
|
||||
|
||||
|
||||
extern CScript COINBASE_FLAGS;
|
||||
|
||||
+1
-1
@@ -365,7 +365,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
|
||||
// Fill in header
|
||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||
pblock->nTime = max(pindexPrev->GetPastTimeLimit()+1, pblock->GetMaxTransactionTime());
|
||||
pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime()));
|
||||
pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime(), pindexPrev->nHeight + 1));
|
||||
if (!fProofOfStake)
|
||||
pblock->UpdateTime(pindexPrev);
|
||||
pblock->nNonce = 0;
|
||||
|
||||
+33
-10
@@ -878,10 +878,19 @@ void ThreadSocketHandler2(void* parg)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vNodes.size() != nPrevNodeCount)
|
||||
{
|
||||
nPrevNodeCount = vNodes.size();
|
||||
uiInterface.NotifyNumConnectionsChanged(vNodes.size());
|
||||
// Read vNodes.size() under the lock to avoid data race
|
||||
unsigned int nNodeCount;
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
nNodeCount = vNodes.size();
|
||||
}
|
||||
if (nNodeCount != nPrevNodeCount)
|
||||
{
|
||||
nPrevNodeCount = nNodeCount;
|
||||
if (!fShutdown)
|
||||
uiInterface.NotifyNumConnectionsChanged(nNodeCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1013,7 +1022,7 @@ void ThreadSocketHandler2(void* parg)
|
||||
for (CNode* pnode : vNodesCopy)
|
||||
{
|
||||
if (fShutdown)
|
||||
return;
|
||||
break;
|
||||
|
||||
//
|
||||
// Receive
|
||||
@@ -1039,7 +1048,7 @@ void ThreadSocketHandler2(void* parg)
|
||||
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
|
||||
pnode->CloseSocketDisconnect();
|
||||
pnode->nLastRecv = GetTime();
|
||||
pnode->nRecvBytes += nBytes;
|
||||
pnode->nRecvBytes += nBytes;
|
||||
}
|
||||
else if (nBytes == 0)
|
||||
{
|
||||
@@ -1104,6 +1113,8 @@ void ThreadSocketHandler2(void* parg)
|
||||
pnode->Release();
|
||||
}
|
||||
|
||||
if (fShutdown)
|
||||
return;
|
||||
MilliSleep(10);
|
||||
}
|
||||
}
|
||||
@@ -1832,6 +1843,9 @@ void ThreadMessageHandler2(void* parg)
|
||||
pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
|
||||
for (CNode* pnode : vNodesCopy)
|
||||
{
|
||||
if (fShutdown)
|
||||
break;
|
||||
|
||||
// Receive messages
|
||||
{
|
||||
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
|
||||
@@ -1839,8 +1853,6 @@ void ThreadMessageHandler2(void* parg)
|
||||
if (!ProcessMessages(pnode))
|
||||
pnode->CloseSocketDisconnect();
|
||||
}
|
||||
if (fShutdown)
|
||||
return;
|
||||
|
||||
// Send messages
|
||||
{
|
||||
@@ -1848,8 +1860,6 @@ void ThreadMessageHandler2(void* parg)
|
||||
if (lockSend)
|
||||
SendMessages(pnode, pnode == pnodeTrickle);
|
||||
}
|
||||
if (fShutdown)
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2180,6 +2190,18 @@ bool StopNode()
|
||||
}
|
||||
MilliSleep(50);
|
||||
DumpAddresses();
|
||||
|
||||
// Force-disconnect and clean up all remaining nodes now that threads have stopped.
|
||||
// Close sockets first so any lingering I/O fails immediately.
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
{
|
||||
pnode->CloseSocketDisconnect();
|
||||
pnode->Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2191,7 +2213,8 @@ public:
|
||||
}
|
||||
~CNetCleanup()
|
||||
{
|
||||
// Close sockets
|
||||
// Close sockets - acquire lock in case other threads are still winding down
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
if (pnode->hSocket != INVALID_SOCKET)
|
||||
closesocket(pnode->hSocket);
|
||||
|
||||
@@ -193,13 +193,16 @@ static void NotifyBlocksChanged(ClientModel *clientmodel)
|
||||
|
||||
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
|
||||
{
|
||||
// Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections);
|
||||
// Don't queue UI updates during shutdown - ClientModel may be destroyed
|
||||
// before Qt processes the queued invocation, causing use-after-free.
|
||||
if (fShutdown) return;
|
||||
QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
|
||||
Q_ARG(int, newNumConnections));
|
||||
}
|
||||
|
||||
static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status)
|
||||
{
|
||||
if (fShutdown) return;
|
||||
OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status);
|
||||
QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::fromStdString(hash.GetHex())),
|
||||
|
||||
+230
-46
@@ -18,6 +18,7 @@
|
||||
#endif
|
||||
|
||||
#include "onion_v3.h"
|
||||
#include "tor_embedded.h"
|
||||
#include "tor_crypto_compat.h"
|
||||
#include "../util.h"
|
||||
#include "../net.h"
|
||||
@@ -59,6 +60,53 @@ extern CWallet* pwalletMain;
|
||||
CTorV3Manager* CTorV3Manager::instance = nullptr;
|
||||
static TorV3Config torV3Config;
|
||||
|
||||
static boost::filesystem::path GetBackendHiddenServiceDir(const std::string& torDataDir)
|
||||
{
|
||||
return boost::filesystem::path(torDataDir) / "hidden_service";
|
||||
}
|
||||
|
||||
static bool ReadTrimmedFirstLine(const boost::filesystem::path& path, std::string& valueOut)
|
||||
{
|
||||
valueOut.clear();
|
||||
|
||||
std::ifstream file(path.string().c_str());
|
||||
if (!file.is_open() || !std::getline(file, valueOut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!valueOut.empty()) {
|
||||
const char ch = valueOut[valueOut.size() - 1];
|
||||
if (ch != '\n' && ch != '\r' && ch != ' ' && ch != '\t') {
|
||||
break;
|
||||
}
|
||||
valueOut.erase(valueOut.size() - 1);
|
||||
}
|
||||
|
||||
return !valueOut.empty();
|
||||
}
|
||||
|
||||
static std::string GetEffectiveTorProxy()
|
||||
{
|
||||
proxyType proxy;
|
||||
if (GetProxy(NET_TOR, proxy)) {
|
||||
return proxy.first.ToStringIPPort();
|
||||
}
|
||||
|
||||
if (mapArgs.count("-tor") && mapArgs["-tor"] != "0") {
|
||||
CService torProxy(mapArgs["-tor"], (unsigned short)GetArg("-torsocks", 19099));
|
||||
if (torProxy.IsValid()) {
|
||||
return torProxy.ToStringIPPort();
|
||||
}
|
||||
}
|
||||
|
||||
std::string explicitProxy = GetArg("-torproxy", "");
|
||||
if (!explicitProxy.empty()) {
|
||||
return explicitProxy;
|
||||
}
|
||||
|
||||
return strprintf("127.0.0.1:%d", GetArg("-torsocks", 19099));
|
||||
}
|
||||
|
||||
// Utility function for proper base32 encoding (RFC 4648) - Tor variant
|
||||
std::string EncodeBase32Proper(const unsigned char* data, size_t len)
|
||||
{
|
||||
@@ -503,40 +551,95 @@ bool CTorV3Service::LoadFromPrivateKey(const std::string& privKey)
|
||||
|
||||
bool CTorV3Service::StartOnionService()
|
||||
{
|
||||
if (onionAddress.empty()) {
|
||||
printf("ERROR: No onion address generated\n");
|
||||
if (!torV3Config.enableTor || !torV3Config.enableHiddenService) {
|
||||
printf("ERROR: Tor hidden service backend is disabled\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use data directory for Tor service files
|
||||
boost::filesystem::path serviceDir = GetDataDir() / "tor_data" / "triangles_v3";
|
||||
boost::filesystem::create_directories(serviceDir);
|
||||
return AttachToBackendService(torV3Config.torDataDirectory, port);
|
||||
}
|
||||
|
||||
// Create Tor configuration for hidden service
|
||||
std::string torrcContent = strprintf(
|
||||
"HiddenServiceDir %s\n"
|
||||
"HiddenServiceVersion 3\n"
|
||||
"HiddenServicePort %d 127.0.0.1:%d\n",
|
||||
serviceDir.string().c_str(), port, port
|
||||
);
|
||||
|
||||
// Write torrc file
|
||||
std::ofstream torrcFile((serviceDir / "torrc").string().c_str());
|
||||
if (torrcFile.is_open()) {
|
||||
torrcFile << torrcContent;
|
||||
torrcFile.close();
|
||||
bool CTorV3Service::AttachToBackendService(const std::string& torDataDir, int servicePort, int waitSeconds)
|
||||
{
|
||||
if (servicePort <= 0 || servicePort > 65535) {
|
||||
printf("ERROR: Invalid backend hidden service port: %d\n", servicePort);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write private 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();
|
||||
if (torDataDir.empty()) {
|
||||
printf("ERROR: Tor data directory is empty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
port = servicePort;
|
||||
|
||||
const boost::filesystem::path serviceDir = GetBackendHiddenServiceDir(torDataDir);
|
||||
const boost::filesystem::path hostnamePath = serviceDir / "hostname";
|
||||
|
||||
std::string backendOnion;
|
||||
for (int waited = 0; waited <= waitSeconds; ++waited) {
|
||||
if (boost::filesystem::exists(hostnamePath) &&
|
||||
ReadTrimmedFirstLine(hostnamePath, backendOnion)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (waited == waitSeconds) {
|
||||
printf("ERROR: Timed out waiting for Tor hidden service hostname at %s\n",
|
||||
hostnamePath.string().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fShutdown) {
|
||||
printf("ERROR: Shutdown requested while waiting for Tor hidden service hostname\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
MilliSleep(1000);
|
||||
}
|
||||
|
||||
if (!ValidateOnionAddress(backendOnion)) {
|
||||
printf("ERROR: Tor backend produced invalid onion address: %s\n", backendOnion.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!onionAddress.empty() && onionAddress != backendOnion) {
|
||||
printf("WARNING: Replacing wallet-managed onion address %s with Tor backend address %s\n",
|
||||
onionAddress.c_str(), backendOnion.c_str());
|
||||
}
|
||||
|
||||
onionAddress = backendOnion;
|
||||
isActive = true;
|
||||
printf("Started V3 onion service on %s:%d\n", onionAddress.c_str(), port);
|
||||
|
||||
if (pwalletMain) {
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile);
|
||||
walletdb.WriteSetting("tor_v3_onion_address", onionAddress);
|
||||
|
||||
// Back up the Tor-generated secret key to wallet.dat so the onion
|
||||
// identity survives deletion of the tor_data directory.
|
||||
boost::filesystem::path secretKeyPath = serviceDir / "hs_ed25519_secret_key";
|
||||
if (boost::filesystem::exists(secretKeyPath)) {
|
||||
std::ifstream keyFile(secretKeyPath.string().c_str(), std::ios::binary);
|
||||
if (keyFile.is_open()) {
|
||||
std::vector<unsigned char> keyData(
|
||||
(std::istreambuf_iterator<char>(keyFile)),
|
||||
std::istreambuf_iterator<char>());
|
||||
keyFile.close();
|
||||
|
||||
if (keyData.size() == 96) {
|
||||
walletdb.WriteSetting("tor_v3_hs_secret_key_backup", keyData);
|
||||
printf("Backed up Tor hidden service secret key to wallet (%d bytes)\n",
|
||||
(int)keyData.size());
|
||||
} else {
|
||||
printf("WARNING: hs_ed25519_secret_key has unexpected size %d (expected 96), not backing up\n",
|
||||
(int)keyData.size());
|
||||
}
|
||||
|
||||
OPENSSL_cleanse(keyData.data(), keyData.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Attached V3 onion service to Tor backend at %s:%d\n", onionAddress.c_str(), port);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1142,20 +1245,10 @@ void CTorV3Manager::ShutdownTor()
|
||||
bool CTorV3Manager::CreateWalletHiddenService(int port)
|
||||
{
|
||||
CTorV3Service* service = new CTorV3Service();
|
||||
|
||||
// Try to load existing service first
|
||||
if (!service->LoadFromWallet()) {
|
||||
// Generate new service
|
||||
if (!service->GenerateV3Service(port)) {
|
||||
delete service;
|
||||
return false;
|
||||
}
|
||||
service->SaveToWallet();
|
||||
}
|
||||
|
||||
if (service->StartOnionService()) {
|
||||
|
||||
if (service->AttachToBackendService(torDataDir, port)) {
|
||||
services[port] = service;
|
||||
printf("Wallet hidden service created: %s\n", service->GetOnionAddress().c_str());
|
||||
printf("Wallet hidden service attached: %s\n", service->GetOnionAddress().c_str());
|
||||
|
||||
// If seeder mode is enabled, automatically register as seeder
|
||||
if (torV3Config.enableSeederMode) {
|
||||
@@ -1968,12 +2061,12 @@ void CTorV3Manager::UpdateDiscoveryStats(int connected, int attempted)
|
||||
bool LoadTorV3Config()
|
||||
{
|
||||
// Tor V3 identity is innate to Triangles — enabled by default
|
||||
torV3Config.enableTor = GetBoolArg("-tor", true);
|
||||
torV3Config.enableHiddenService = GetBoolArg("-torhiddenservice", true);
|
||||
torV3Config.enableTor = !GetBoolArg("-notor", false);
|
||||
torV3Config.enableHiddenService = torV3Config.enableTor && GetBoolArg("-torhiddenservice", true);
|
||||
torV3Config.enableSeederMode = GetBoolArg("-torseeder", false);
|
||||
torV3Config.hiddenServicePort = GetArg("-torhiddenserviceport", GetDefaultPort());
|
||||
torV3Config.hiddenServicePort = GetArg("-torhsport", GetListenPort());
|
||||
torV3Config.torDataDirectory = GetArg("-tordatadir", (GetDataDir() / "tor_data").string());
|
||||
torV3Config.socksProxy = GetArg("-torproxy", "127.0.0.1:9050");
|
||||
torV3Config.socksProxy = GetEffectiveTorProxy();
|
||||
torV3Config.maxConnections = GetArg("-tormaxconnections", 8);
|
||||
|
||||
printf("Loaded Tor V3 configuration: enabled=%s, hidden_service=%s, seeder=%s, proxy=%s\n",
|
||||
@@ -2093,15 +2186,106 @@ void CTorV3Manager::RequestSeederListFromPeer(const std::string& peerAddress)
|
||||
// Schedule periodic seeder re-announcements
|
||||
void CTorV3Manager::ScheduleSeederReannouncement()
|
||||
{
|
||||
// This would typically be handled by a timer or scheduler
|
||||
// For now, we'll just update the last announcement time
|
||||
if (pwalletMain) {
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile);
|
||||
walletdb.WriteSetting("seeder_last_announcement", (int64_t)GetTime());
|
||||
printf("Scheduled seeder re-announcement\n");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Background thread: Tor health monitoring + seeder maintenance
|
||||
// ---------------------------------------------------------------------------
|
||||
void ThreadTorMaintenance(void* parg)
|
||||
{
|
||||
RenameThread("Triangles-tormaint");
|
||||
printf("Tor maintenance thread started\n");
|
||||
|
||||
int restartBackoffSec = 30;
|
||||
int64_t lastSeederMaint = GetTime();
|
||||
static const int SEEDER_INTERVAL = 1800; // 30 minutes
|
||||
|
||||
while (!fShutdown)
|
||||
{
|
||||
// Sleep in short intervals so the thread exits promptly on shutdown
|
||||
for (int i = 0; i < 60 && !fShutdown; i++)
|
||||
MilliSleep(500);
|
||||
if (fShutdown) break;
|
||||
|
||||
// --- Tor health check & auto-restart ---
|
||||
if (!CTorEmbedded::GetInstance()->IsRunning())
|
||||
{
|
||||
printf("WARNING: Tor process is no longer running, attempting restart...\n");
|
||||
|
||||
if (StartEmbeddedTor())
|
||||
{
|
||||
printf("Tor restarted successfully\n");
|
||||
restartBackoffSec = 30;
|
||||
|
||||
// Re-attach the hidden service identity
|
||||
TorV3Config& torConfig = GetTorV3Config();
|
||||
std::string torDataPath = CTorEmbedded::GetInstance()->GetDataDir();
|
||||
if (torDataPath.empty())
|
||||
torDataPath = torConfig.torDataDirectory;
|
||||
|
||||
torConfig.enableTor = true;
|
||||
torConfig.enableHiddenService = CTorEmbedded::GetInstance()->IsHiddenServiceEnabled();
|
||||
torConfig.hiddenServicePort = CTorEmbedded::GetInstance()->GetHiddenServicePort();
|
||||
torConfig.torDataDirectory = torDataPath;
|
||||
|
||||
if (torConfig.enableHiddenService && InitTorV3())
|
||||
{
|
||||
std::string onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
|
||||
if (!onionAddr.empty())
|
||||
{
|
||||
AddLocal(CService(onionAddr, torConfig.hiddenServicePort), LOCAL_MANUAL);
|
||||
printf("Re-registered Tor V3 identity after restart: %s\n", onionAddr.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("WARNING: Tor restart failed, retrying in %d seconds\n", restartBackoffSec);
|
||||
for (int i = 0; i < restartBackoffSec * 2 && !fShutdown; i++)
|
||||
MilliSleep(500);
|
||||
if (restartBackoffSec < 300)
|
||||
restartBackoffSec *= 2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- Seeder maintenance (every 30 minutes) ---
|
||||
TorV3Config& cfg = GetTorV3Config();
|
||||
if (cfg.enableSeederMode && (GetTime() - lastSeederMaint) >= SEEDER_INTERVAL)
|
||||
{
|
||||
CTorV3Manager* mgr = CTorV3Manager::GetInstance();
|
||||
std::string ownAddr = mgr->GetWalletOnionAddress();
|
||||
|
||||
if (!ownAddr.empty())
|
||||
{
|
||||
// Re-announce ourselves as a seeder to all peers
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
{
|
||||
try {
|
||||
pnode->PushMessage("seeder", ownAddr, cfg.hiddenServicePort);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
printf("Seeder re-announcement sent to %d peers\n", (int)vNodes.size());
|
||||
}
|
||||
|
||||
// Refresh our knowledge of other seeders
|
||||
mgr->RequestSeederListFromPeers();
|
||||
|
||||
mgr->ScheduleSeederReannouncement();
|
||||
lastSeederMaint = GetTime();
|
||||
}
|
||||
}
|
||||
|
||||
printf("Tor maintenance thread exited\n");
|
||||
}
|
||||
|
||||
// Global functions
|
||||
bool InitTorV3()
|
||||
{
|
||||
@@ -2111,4 +2295,4 @@ bool InitTorV3()
|
||||
void ShutdownTorV3()
|
||||
{
|
||||
CTorV3Manager::GetInstance()->ShutdownTor();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -34,7 +34,10 @@ public:
|
||||
|
||||
// Start the onion service
|
||||
bool StartOnionService();
|
||||
|
||||
|
||||
// Attach to the hidden service managed by the running Tor backend
|
||||
bool AttachToBackendService(const std::string& torDataDir, int servicePort, int waitSeconds = 30);
|
||||
|
||||
// Stop the onion service
|
||||
bool StopService();
|
||||
|
||||
@@ -169,5 +172,6 @@ void ShutdownTorV3();
|
||||
TorV3Config& GetTorV3Config();
|
||||
bool SaveTorV3Config();
|
||||
bool LoadTorV3Config();
|
||||
void ThreadTorMaintenance(void* parg);
|
||||
|
||||
#endif // TRIANGLES_TOR_ONION_V3_H
|
||||
#endif // TRIANGLES_TOR_ONION_V3_H
|
||||
|
||||
+35
-23
@@ -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()
|
||||
|
||||
@@ -16,6 +16,7 @@ private:
|
||||
std::atomic<bool> running;
|
||||
int socksPort;
|
||||
int hiddenServicePort;
|
||||
bool hiddenServiceEnabled;
|
||||
std::string torDataDir;
|
||||
std::string onionHostname;
|
||||
|
||||
@@ -26,7 +27,7 @@ public:
|
||||
~CTorEmbedded();
|
||||
|
||||
// Start Tor in a background thread (blocks that thread until shutdown)
|
||||
bool Start(int socksPort = 19099, int hsPort = 24112);
|
||||
bool Start(int socksPort = 19099, int hsPort = 24112, bool enableHiddenService = true);
|
||||
|
||||
// Request Tor to shut down
|
||||
void Stop();
|
||||
@@ -45,6 +46,7 @@ public:
|
||||
|
||||
// Get the hidden service port
|
||||
int GetHiddenServicePort() const { return hiddenServicePort; }
|
||||
bool IsHiddenServiceEnabled() const { return hiddenServiceEnabled; }
|
||||
};
|
||||
|
||||
// Global init/shutdown hooks (called from init.cpp)
|
||||
|
||||
+28
-17
@@ -48,6 +48,7 @@ CTorProcess* CTorProcess::GetInstance()
|
||||
CTorProcess::CTorProcess()
|
||||
: socksPort(19099)
|
||||
, hiddenServicePort(24112)
|
||||
, hiddenServiceEnabled(true)
|
||||
, running(false)
|
||||
#ifdef WIN32
|
||||
, hProcess(NULL)
|
||||
@@ -198,11 +199,13 @@ bool CTorProcess::WriteTorrc()
|
||||
fs::create_directories(torStateDir);
|
||||
torrc << "DataDirectory " << torStateDir.string() << "\n";
|
||||
|
||||
// V3 hidden service so this node is reachable via .onion
|
||||
torrc << "HiddenServiceDir " << hsDir.string() << "\n";
|
||||
torrc << "HiddenServiceVersion 3\n";
|
||||
torrc << "HiddenServicePort " << hiddenServicePort
|
||||
<< " 127.0.0.1:" << hiddenServicePort << "\n";
|
||||
if (hiddenServiceEnabled) {
|
||||
// V3 hidden service so this node is reachable via .onion
|
||||
torrc << "HiddenServiceDir " << hsDir.string() << "\n";
|
||||
torrc << "HiddenServiceVersion 3\n";
|
||||
torrc << "HiddenServicePort " << hiddenServicePort
|
||||
<< " 127.0.0.1:" << hiddenServicePort << "\n";
|
||||
}
|
||||
|
||||
// Reduce bandwidth/resource usage for wallet use
|
||||
torrc << "ClientOnly 1\n";
|
||||
@@ -213,15 +216,21 @@ bool CTorProcess::WriteTorrc()
|
||||
|
||||
torrc.close();
|
||||
|
||||
printf("Wrote torrc to %s (SOCKS %d, HS port %d)\n",
|
||||
torrcPath.c_str(), socksPort, hiddenServicePort);
|
||||
if (hiddenServiceEnabled) {
|
||||
printf("Wrote torrc to %s (SOCKS %d, HS port %d)\n",
|
||||
torrcPath.c_str(), socksPort, hiddenServicePort);
|
||||
} else {
|
||||
printf("Wrote torrc to %s (SOCKS %d, hidden service disabled)\n",
|
||||
torrcPath.c_str(), socksPort);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort)
|
||||
bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool enableHiddenService)
|
||||
{
|
||||
socksPort = socks;
|
||||
hiddenServicePort = hsPort;
|
||||
hiddenServiceEnabled = enableHiddenService;
|
||||
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
|
||||
torDataDir = dataDir;
|
||||
|
||||
// Check if something is already listening on our SOCKS port
|
||||
@@ -314,12 +323,14 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort)
|
||||
printf("Tor SOCKS proxy ready on port %d (took %ds)\n", socksPort, i + 1);
|
||||
|
||||
// Read and display the hidden service hostname if available
|
||||
fs::path hsHostname = fs::path(torDataDir) / "hidden_service" / "hostname";
|
||||
if (fs::exists(hsHostname)) {
|
||||
std::ifstream f(hsHostname.string().c_str());
|
||||
std::string hostname;
|
||||
if (f.is_open() && std::getline(f, hostname)) {
|
||||
printf("Tor hidden service: %s\n", hostname.c_str());
|
||||
if (hiddenServiceEnabled) {
|
||||
fs::path hsHostname = fs::path(torDataDir) / "hidden_service" / "hostname";
|
||||
if (fs::exists(hsHostname)) {
|
||||
std::ifstream f(hsHostname.string().c_str());
|
||||
std::string hostname;
|
||||
if (f.is_open() && std::getline(f, hostname)) {
|
||||
printf("Tor hidden service: %s\n", hostname.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -402,9 +413,9 @@ std::string CTorProcess::GetSocksProxy() const
|
||||
}
|
||||
|
||||
// Global convenience functions
|
||||
bool StartTorProcess(const std::string& dataDir, int socksPort, int hsPort)
|
||||
bool StartTorProcess(const std::string& dataDir, int socksPort, int hsPort, bool enableHiddenService)
|
||||
{
|
||||
return CTorProcess::GetInstance()->Start(dataDir, socksPort, hsPort);
|
||||
return CTorProcess::GetInstance()->Start(dataDir, socksPort, hsPort, enableHiddenService);
|
||||
}
|
||||
|
||||
void StopTorProcess()
|
||||
|
||||
@@ -21,6 +21,7 @@ private:
|
||||
std::string torrcPath;
|
||||
int socksPort;
|
||||
int hiddenServicePort;
|
||||
bool hiddenServiceEnabled;
|
||||
bool running;
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
|
||||
// Start the Tor process
|
||||
// Returns true if Tor was started or is already running
|
||||
bool Start(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112);
|
||||
bool Start(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112, bool enableHiddenService = true);
|
||||
|
||||
// Stop the Tor process
|
||||
void Stop();
|
||||
@@ -64,7 +65,7 @@ public:
|
||||
};
|
||||
|
||||
// Global convenience functions
|
||||
bool StartTorProcess(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112);
|
||||
bool StartTorProcess(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112, bool enableHiddenService = true);
|
||||
void StopTorProcess();
|
||||
|
||||
#endif // TRIANGLES_TOR_PROCESS_H
|
||||
|
||||
+17
-2
@@ -946,9 +946,24 @@ void ThreadRPCServer2(void* parg)
|
||||
|
||||
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||
while (!fShutdown)
|
||||
io_service.run_one();
|
||||
{
|
||||
// Use poll_one + sleep instead of blocking run_one so the thread
|
||||
// remains responsive to fShutdown and can exit promptly.
|
||||
if (!io_service.poll_one())
|
||||
{
|
||||
io_service.restart();
|
||||
MilliSleep(50);
|
||||
}
|
||||
}
|
||||
vnThreadsRunning[THREAD_RPCLISTENER]++;
|
||||
StopRequests();
|
||||
|
||||
// Safely shut down: close acceptors, then drain any remaining handlers
|
||||
try {
|
||||
StopRequests();
|
||||
} catch (...) {
|
||||
// Absorb bad_weak_ptr or other exceptions from stale tracked slots
|
||||
}
|
||||
io_service.poll(); // process cancellation callbacks so shared_ptrs are released
|
||||
}
|
||||
|
||||
class JSONRequest
|
||||
|
||||
+8
-5
@@ -486,7 +486,8 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
|
||||
printf("WalletUpdateSpent found spent coin %s TRI %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
|
||||
wtx.MarkSpent(txin.prevout.n);
|
||||
wtx.WriteToDisk();
|
||||
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
|
||||
if (!IsInitialBlockDownload())
|
||||
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -503,7 +504,8 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
|
||||
{
|
||||
wtx.MarkUnspent(&txout - &tx.vout[0]);
|
||||
wtx.WriteToDisk();
|
||||
NotifyTransactionChanged(this, hash, CT_UPDATED);
|
||||
if (!IsInitialBlockDownload())
|
||||
NotifyTransactionChanged(this, hash, CT_UPDATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,8 +636,9 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
|
||||
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
|
||||
WalletUpdateSpent(wtx, (wtxIn.hashBlock != 0));
|
||||
|
||||
// Notify UI of new or updated transaction
|
||||
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
|
||||
// Notify UI of new or updated transaction (skip during IBD to avoid flooding the event loop)
|
||||
if (!IsInitialBlockDownload())
|
||||
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
|
||||
|
||||
// notify an external script when a wallet transaction comes in or is updated
|
||||
std::string strCmd = GetArg("-walletnotify", "");
|
||||
@@ -2775,7 +2778,7 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
|
||||
LOCK(cs_wallet);
|
||||
// Only notify UI if this transaction is in this wallet
|
||||
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
|
||||
if (mi != mapWallet.end())
|
||||
if (mi != mapWallet.end() && !IsInitialBlockDownload())
|
||||
NotifyTransactionChanged(this, hashTx, CT_UPDATED);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user