Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -153,14 +153,15 @@ jobs:
|
||||
New-Item -ItemType Directory -Path tor-extract -Force
|
||||
tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
New-Item -ItemType Directory -Path tor-files -Force
|
||||
Copy-Item tor-extract/tor/tor.exe tor-files/
|
||||
Copy-Item tor-extract/tor/tor-gencert.exe tor-files/ -ErrorAction SilentlyContinue
|
||||
Copy-Item -Recurse tor-extract/tor/* tor-files/
|
||||
if (Test-Path tor-extract/tor/pluggable_transports) {
|
||||
Copy-Item -Recurse tor-extract/tor/pluggable_transports tor-files/
|
||||
Copy-Item -Recurse tor-extract/tor/pluggable_transports tor-files/pluggable_transports -Force
|
||||
}
|
||||
if (Test-Path tor-extract/data) {
|
||||
Copy-Item -Recurse tor-extract/data tor-files/data
|
||||
}
|
||||
Write-Host "Bundled Tor runtime files:"
|
||||
Get-ChildItem -Recurse tor-files | Select-Object FullName
|
||||
|
||||
- name: Install NSIS via MSYS2
|
||||
run: pacman -S --noconfirm mingw-w64-x86_64-nsis
|
||||
@@ -240,7 +241,7 @@ jobs:
|
||||
Invoke-WebRequest -Uri "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-windows-x86_64-${TOR_VERSION}.tar.gz" -OutFile tor-bundle.tar.gz
|
||||
New-Item -ItemType Directory -Path tor-extract -Force
|
||||
tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
Copy-Item tor-extract/tor/tor.exe daemon-dist/tor/
|
||||
Copy-Item -Recurse tor-extract/tor/* daemon-dist/tor/
|
||||
if (Test-Path tor-extract/data) {
|
||||
Copy-Item -Recurse tor-extract/data daemon-dist/tor/data
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
// 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 7
|
||||
#define CLIENT_VERSION_REVISION 6
|
||||
#define CLIENT_VERSION_REVISION 7
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
+4
-1
@@ -1223,7 +1223,10 @@ bool AppInit2()
|
||||
fUseUPnP = false;
|
||||
#endif
|
||||
} else {
|
||||
return InitError(_("Tor failed to start. Triangles requires Tor to operate."));
|
||||
std::string torError = CTorEmbedded::GetInstance()->GetLastError();
|
||||
if (torError.empty())
|
||||
torError = "No detailed Tor startup error was recorded.";
|
||||
return InitError(strprintf(_("Tor failed to start. Triangles requires Tor to operate.\n\nDetails: %s"), torError));
|
||||
}
|
||||
|
||||
// Initialize Tor V3 identity (Ed25519 keys, onion address)
|
||||
|
||||
@@ -678,6 +678,43 @@ QWidget#line {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="labelOnionAddressText">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Wallet Onion:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="labelOnionAddress">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>This wallet's Tor .onion address.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">Initializing Tor identity...</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
#include "transactionfilterproxy.h"
|
||||
#include "guiutil.h"
|
||||
#include "guiconstants.h"
|
||||
#include "tor/tor_embedded.h"
|
||||
#include "tor/onion_v3.h"
|
||||
|
||||
#include <QAbstractItemDelegate>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
|
||||
#define DECORATION_SIZE 64
|
||||
#define NUM_ITEMS 5
|
||||
@@ -88,6 +91,7 @@ public:
|
||||
OverviewPage::OverviewPage(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OverviewPage),
|
||||
clientModel(0),
|
||||
currentBalance(-1),
|
||||
currentStake(0),
|
||||
currentUnconfirmedBalance(-1),
|
||||
@@ -112,6 +116,11 @@ OverviewPage::OverviewPage(QWidget *parent) :
|
||||
|
||||
// start with displaying the "out of sync" warnings
|
||||
showOutOfSyncWarning(true);
|
||||
|
||||
QTimer *onionRefreshTimer = new QTimer(this);
|
||||
connect(onionRefreshTimer, SIGNAL(timeout()), this, SLOT(updateOnionAddress()));
|
||||
onionRefreshTimer->start(5000);
|
||||
updateOnionAddress();
|
||||
}
|
||||
|
||||
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
|
||||
@@ -125,6 +134,12 @@ OverviewPage::~OverviewPage()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OverviewPage::setClientModel(ClientModel *clientModel)
|
||||
{
|
||||
this->clientModel = clientModel;
|
||||
updateOnionAddress();
|
||||
}
|
||||
|
||||
void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
|
||||
{
|
||||
if (!model || !model->getOptionsModel())
|
||||
@@ -213,3 +228,20 @@ void OverviewPage::showOutOfSyncWarning(bool fShow)
|
||||
ui->labelWalletStatus->setVisible(fShow);
|
||||
ui->labelTransactionsStatus->setVisible(fShow);
|
||||
}
|
||||
|
||||
void OverviewPage::updateOnionAddress()
|
||||
{
|
||||
std::string onionAddress = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
|
||||
if (onionAddress.empty()) {
|
||||
onionAddress = CTorEmbedded::GetInstance()->GetOnionAddress();
|
||||
}
|
||||
|
||||
if (onionAddress.empty()) {
|
||||
ui->labelOnionAddress->setText(tr("Initializing Tor identity..."));
|
||||
ui->labelOnionAddress->setToolTip(tr("The wallet's .onion address will appear here once Tor and the wallet identity are ready."));
|
||||
return;
|
||||
}
|
||||
|
||||
ui->labelOnionAddress->setText(QString::fromStdString(onionAddress));
|
||||
ui->labelOnionAddress->setToolTip(tr("This wallet's Tor .onion address."));
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
public slots:
|
||||
void setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
|
||||
void setTransactionSyncState(bool syncing);
|
||||
void updateOnionAddress();
|
||||
|
||||
signals:
|
||||
void transactionClicked(const QModelIndex &index);
|
||||
|
||||
@@ -567,6 +567,7 @@ void TrianglesGUI::setClientModel(ClientModel *clientModel)
|
||||
|
||||
if (rpcConsole)
|
||||
rpcConsole->setClientModel(clientModel);
|
||||
overviewPage->setClientModel(clientModel);
|
||||
addressBookPage->setOptionsModel(clientModel->getOptionsModel());
|
||||
receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel());
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
|
||||
{
|
||||
if (running.load()) return true;
|
||||
|
||||
lastError.clear();
|
||||
socksPort = socks;
|
||||
hiddenServiceEnabled = enableHiddenService;
|
||||
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
|
||||
@@ -202,11 +203,13 @@ bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
|
||||
}
|
||||
|
||||
if (!running.load()) {
|
||||
lastError = "Embedded Tor thread exited during bootstrap before the SOCKS proxy became available.";
|
||||
printf("ERROR: Embedded Tor thread exited during bootstrap\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lastError = strprintf("Embedded Tor did not expose SOCKS port %d within 60 seconds.", socksPort);
|
||||
printf("WARNING: Embedded Tor started but SOCKS not ready after 60s (still bootstrapping)\n");
|
||||
return true;
|
||||
}
|
||||
@@ -241,8 +244,12 @@ bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
|
||||
hiddenServiceEnabled = enableHiddenService;
|
||||
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
|
||||
onionHostname.clear();
|
||||
lastError.clear();
|
||||
torDataDir = (::GetDataDir() / "tor_data").string();
|
||||
running.store(StartTorProcess(torDataDir, socksPort, hiddenServicePort, hiddenServiceEnabled));
|
||||
if (!running.load()) {
|
||||
lastError = CTorProcess::GetInstance()->GetLastError();
|
||||
}
|
||||
return running.load();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ private:
|
||||
bool hiddenServiceEnabled;
|
||||
std::string torDataDir;
|
||||
std::string onionHostname;
|
||||
std::string lastError;
|
||||
|
||||
public:
|
||||
static CTorEmbedded* GetInstance();
|
||||
@@ -43,6 +44,8 @@ public:
|
||||
|
||||
// Get our .onion address (available after bootstrap)
|
||||
std::string GetOnionAddress() const { return onionHostname; }
|
||||
std::string GetLastError() const { return lastError; }
|
||||
void SetLastError(const std::string& value) { lastError = value; }
|
||||
|
||||
// Get the hidden service port
|
||||
int GetHiddenServicePort() const { return hiddenServicePort; }
|
||||
|
||||
+44
-1
@@ -19,11 +19,13 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -35,6 +37,28 @@
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
static std::string ReadTailLines(const fs::path& filePath, size_t maxLines)
|
||||
{
|
||||
std::ifstream in(filePath.string().c_str());
|
||||
if (!in.is_open()) return "";
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
lines.push_back(line);
|
||||
if (lines.size() > maxLines) {
|
||||
lines.erase(lines.begin());
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream out;
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (i) out << " | ";
|
||||
out << lines[i];
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
static CTorProcess* torProcessInstance = nullptr;
|
||||
|
||||
CTorProcess* CTorProcess::GetInstance()
|
||||
@@ -199,6 +223,10 @@ bool CTorProcess::WriteTorrc()
|
||||
fs::create_directories(torStateDir);
|
||||
torrc << "DataDirectory " << torStateDir.string() << "\n";
|
||||
|
||||
// Persistent Tor log for post-mortem debugging on user machines.
|
||||
fs::path torLogPath = dataPath / "tor.log";
|
||||
torrc << "Log notice file " << torLogPath.string() << "\n";
|
||||
|
||||
if (hiddenServiceEnabled) {
|
||||
// V3 hidden service so this node is reachable via .onion
|
||||
torrc << "HiddenServiceDir " << hsDir.string() << "\n";
|
||||
@@ -232,6 +260,7 @@ bool CTorProcess::WriteTorrc()
|
||||
|
||||
bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool enableHiddenService)
|
||||
{
|
||||
lastError.clear();
|
||||
socksPort = socks;
|
||||
hiddenServiceEnabled = enableHiddenService;
|
||||
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
|
||||
@@ -240,6 +269,7 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
|
||||
// Check if something is already listening on our SOCKS port
|
||||
if (IsPortInUse(socksPort)) {
|
||||
printf("Tor SOCKS port %d already in use - assuming Tor is running\n", socksPort);
|
||||
lastError = strprintf("SOCKS port %d is already in use; assuming an existing Tor instance is serving it.", socksPort);
|
||||
running = true;
|
||||
return true;
|
||||
}
|
||||
@@ -247,6 +277,7 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
|
||||
// Find Tor binary
|
||||
torBinaryPath = FindTorBinary();
|
||||
if (torBinaryPath.empty()) {
|
||||
lastError = "Tor binary was not found in the bundled install or standard search paths.";
|
||||
printf("WARNING: Tor binary not found. Install Tor for .onion connectivity.\n");
|
||||
printf(" Windows: Download from https://www.torproject.org/download/tor/\n");
|
||||
printf(" Linux: apt install tor or yum install tor\n");
|
||||
@@ -256,6 +287,7 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
|
||||
|
||||
// Write configuration
|
||||
if (!WriteTorrc()) {
|
||||
lastError = strprintf("Failed to write Tor configuration to %s", torrcPath.c_str());
|
||||
printf("ERROR: Failed to write Tor configuration\n");
|
||||
return false;
|
||||
}
|
||||
@@ -282,7 +314,9 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
|
||||
NULL, NULL,
|
||||
&si, &pi))
|
||||
{
|
||||
printf("ERROR: Failed to start Tor process (error %lu)\n", GetLastError());
|
||||
DWORD err = GetLastError();
|
||||
lastError = strprintf("CreateProcess failed for Tor binary '%s' with Windows error %lu", torBinaryPath.c_str(), err);
|
||||
printf("ERROR: Failed to start Tor process (error %lu)\n", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -329,6 +363,7 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
|
||||
// Defensive check: Tor must not directly occupy the node's hidden-service
|
||||
// virtual port. If it does, node startup will fail with a bind collision.
|
||||
if (hiddenServiceEnabled && IsPortInUse(hiddenServicePort)) {
|
||||
lastError = strprintf("Port %d is already busy while Tor hidden service is enabled. Another process is likely blocking the node listener.", hiddenServicePort);
|
||||
printf("ERROR: Tor startup collision: hidden service port %d appears busy before node bind.\n",
|
||||
hiddenServicePort);
|
||||
printf(" Refusing to treat Tor as healthy because this would block the node listener.\n");
|
||||
@@ -353,12 +388,20 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool
|
||||
|
||||
// Check if Tor process is still alive
|
||||
if (!IsRunning()) {
|
||||
fs::path torLogPath = fs::path(torDataDir) / "tor.log";
|
||||
std::string torLogTail = ReadTailLines(torLogPath, 8);
|
||||
if (!torLogTail.empty()) {
|
||||
lastError = strprintf("Tor process exited during bootstrap before the SOCKS port became ready. Recent tor.log: %s", torLogTail.c_str());
|
||||
} else {
|
||||
lastError = "Tor process exited during bootstrap before the SOCKS port became ready.";
|
||||
}
|
||||
printf("ERROR: Tor process exited prematurely\n");
|
||||
running = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lastError = strprintf("Tor process started from '%s' but SOCKS port %d was not ready after 30 seconds.", torBinaryPath.c_str(), socksPort);
|
||||
printf("WARNING: Tor started but SOCKS proxy not yet ready after 30s\n");
|
||||
printf(" Tor may still be bootstrapping. .onion connections will work once ready.\n");
|
||||
return true;
|
||||
|
||||
@@ -19,6 +19,7 @@ private:
|
||||
std::string torBinaryPath;
|
||||
std::string torDataDir;
|
||||
std::string torrcPath;
|
||||
std::string lastError;
|
||||
int socksPort;
|
||||
int hiddenServicePort;
|
||||
bool hiddenServiceEnabled;
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
|
||||
// Get the Tor binary path (for diagnostics)
|
||||
std::string GetBinaryPath() const { return torBinaryPath; }
|
||||
std::string GetLastError() const { return lastError; }
|
||||
|
||||
// Singleton access
|
||||
static CTorProcess* GetInstance();
|
||||
|
||||
Reference in New Issue
Block a user