mirror of
https://github.com/wownero/wowlet.git
synced 2026-10-21 17:18:43 +02:00
Windows crash chain (exposed by the embedded-node default): - main.cpp: force the QtQuick software renderer. The Mining tab's QQuickWidget drove the default RHI/GL path and faulted (0xc0000005) on broken-GL hosts (VirtualBox VMSVGA), crashing the app and orphaning the embedded node. Real hardware was unaffected. - ChildProcessGuard + DaemonManager/TorManager: kernel-level die-with-parent (Windows Job Object KILL_ON_JOB_CLOSE / Linux PR_SET_PDEATHSIG) so the embedded wownerod and bundled tor never survive a wowlet exit or crash. - DaemonManager: verify-before-adopt via get_info (nettype/height) instead of trusting a bare open RPC port, so a stale/orphan node is never adopted. Node/Tor lifecycle + remote-node connectivity: - config: torPrivacyLevel default -> allTorExceptNode. NEVER sync blocks over Tor; the wallet's node connection is always clearnet. Tor is only for tx broadcast, so remote nodes work with or without Tor. - nodes: add Nodes::reconnect() (clear failure blocklist + force reconnect); node/Tor toggle handlers use it so a transient outage no longer strands the wallet at "Disconnected". - MainWindow/SettingsDialog: reconnect on node/Tor toggle and on the async daemonStateChanged (closes the start() race; a freshly-toggled local node connects immediately). - TorManager: reset retry/unpack state in init() so a system<->bundled Tor re-toggle can start again; quiesce on stop(). Linux unpackBins() rework (mkpath + presence-based unpack + graceful return-false), getVersion() arg fix, and a missing-binary start guard. - build.yml: derive TOR_VERSION from the embedded tor binary (a hardcoded bundle number made unpackBins rewrite Tor every launch). - depends/funcs.mk: confine cross-cmake find_package to the depends sysroot so host -dev libraries don't leak into the build.
215 lines
5.4 KiB
C++
215 lines
5.4 KiB
C++
// SPDX-License-Identifier: BSD-3-Clause
|
|
// SPDX-FileCopyrightText: The Monero Project
|
|
|
|
#ifndef FEATHER_NODES_H
|
|
#define FEATHER_NODES_H
|
|
|
|
#include <QObject>
|
|
#include <QJsonObject>
|
|
#include <QTimer>
|
|
#include <QRegularExpression>
|
|
#include <QHostAddress>
|
|
#include <QUrl>
|
|
|
|
#include "model/NodeModel.h"
|
|
#include "utils/Utils.h"
|
|
#include "utils/config.h"
|
|
|
|
class Wallet;
|
|
|
|
enum NodeSource {
|
|
websocket = 0,
|
|
custom
|
|
};
|
|
|
|
class NodeList : public QObject {
|
|
Q_OBJECT
|
|
|
|
public:
|
|
enum Type {
|
|
ws = 0,
|
|
custom
|
|
};
|
|
Q_ENUM(Type)
|
|
|
|
bool addNode(const QString &node, NetworkType::Type networkType, NodeList::Type source);
|
|
void setNodes(const QStringList &nodes, NetworkType::Type networkType, NodeList::Type source);
|
|
QStringList getNodes(NetworkType::Type networkType, NodeList::Type source);
|
|
|
|
private:
|
|
void ensureStructure(QJsonObject &obj, NetworkType::Type networkType);
|
|
QJsonObject getConfigData();
|
|
};
|
|
|
|
struct FeatherNode {
|
|
explicit FeatherNode(QString address = "", int height = 0, int target_height = 0, bool online = false)
|
|
: height(height)
|
|
, target_height(target_height)
|
|
, online(online)
|
|
{
|
|
if (address.isEmpty())
|
|
return;
|
|
|
|
address.remove("https://"); // todo: regex
|
|
if (!address.startsWith("http://"))
|
|
address.prepend("http://");
|
|
|
|
url = QUrl(address);
|
|
|
|
if (!url.isValid())
|
|
return;
|
|
|
|
if (url.port() == -1)
|
|
url.setPort(34568);
|
|
};
|
|
|
|
int height;
|
|
int target_height;
|
|
bool online = false;
|
|
bool cached = false;
|
|
bool custom = false;
|
|
bool isConnecting = false;
|
|
bool isActive = false;
|
|
QUrl url;
|
|
|
|
bool isValid() const {
|
|
return url.isValid();
|
|
}
|
|
|
|
bool isLocal() const {
|
|
QString host = url.host();
|
|
|
|
if (host == "localhost") {
|
|
return true;
|
|
}
|
|
|
|
if (host.endsWith(".local")) { // RFC 6762
|
|
return true;
|
|
}
|
|
|
|
QHostAddress address(host);
|
|
|
|
bool validipv4;
|
|
quint32 ipv4 = address.toIPv4Address(&validipv4);
|
|
|
|
if (!validipv4) {
|
|
return false;
|
|
}
|
|
|
|
bool local = (
|
|
((ipv4 & 0xff000000) == 0x0a000000) || /* 10/8 */
|
|
((ipv4 & 0xff000000) == 0x00000000) || /* 0/8 */
|
|
((ipv4 & 0xff000000) == 0x7f000000) || /* 127/8 */
|
|
((ipv4 & 0xffc00000) == 0x64400000) || /* 100.64/10 */
|
|
((ipv4 & 0xffff0000) == 0xa9fe0000) || /* 169.254/16 */
|
|
((ipv4 & 0xfff00000) == 0xac100000) || /* 172.16/12 */
|
|
((ipv4 & 0xffff0000) == 0xc0a80000)); /* 192.168/16 */
|
|
|
|
return local;
|
|
}
|
|
|
|
bool isOnion() const {
|
|
return url.host().endsWith(".onion");
|
|
}
|
|
|
|
bool isI2P() const {
|
|
return url.host().endsWith(".i2p");
|
|
}
|
|
|
|
bool isAnonymityNetwork() const {
|
|
return isOnion() || isI2P();
|
|
};
|
|
|
|
QString toAddress() const {
|
|
return QString("%1:%2").arg(url.host(), QString::number(url.port()));
|
|
}
|
|
|
|
QString toFullAddress() const {
|
|
if (!url.userName().isEmpty() && !url.password().isEmpty())
|
|
return QString("%1:%2@%3:%4").arg(url.userName(), url.password(), url.host(), QString::number(url.port()));
|
|
|
|
return toAddress();
|
|
}
|
|
|
|
QString toURL() const {
|
|
QUrl withScheme(url);
|
|
withScheme.setScheme("http");
|
|
|
|
return withScheme.toString(QUrl::RemoveUserInfo | QUrl::RemovePath);
|
|
}
|
|
|
|
bool operator == (const FeatherNode &other) const {
|
|
return this->url == other.url;
|
|
}
|
|
};
|
|
|
|
class Networking;
|
|
|
|
class Nodes : public QObject {
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit Nodes(QObject *parent, Wallet *wallet);
|
|
~Nodes() override;
|
|
|
|
void loadConfig();
|
|
void allowConnection();
|
|
void updateModels();
|
|
|
|
NodeSource source();
|
|
FeatherNode connection();
|
|
|
|
QList<FeatherNode> nodes();
|
|
QList<FeatherNode> customNodes();
|
|
QList<FeatherNode> websocketNodes();
|
|
|
|
NodeModel *modelWebsocket;
|
|
NodeModel *modelCustom;
|
|
|
|
public slots:
|
|
void connectToNode();
|
|
void connectToNode(const FeatherNode &node);
|
|
void onWSNodesReceived(QList<FeatherNode>& nodes);
|
|
void onNodeSourceChanged(NodeSource nodeSource);
|
|
void setCustomNodes(const QList<FeatherNode>& nodes);
|
|
void autoConnect(bool forceReconnect = false);
|
|
// wowlet: clear the recent-failure blocklist and force-reconnect. Used by the node/Tor live-toggle
|
|
// handlers so a node blocklisted during a transient outage (e.g. a Tor toggle) is actually retried.
|
|
void reconnect();
|
|
|
|
private slots:
|
|
void onWalletRefreshed();
|
|
|
|
private:
|
|
Wallet *m_wallet = nullptr;
|
|
QJsonObject m_configJson;
|
|
|
|
NodeList m_nodes;
|
|
|
|
QStringList m_recentFailures;
|
|
|
|
QList<FeatherNode> m_customNodes;
|
|
QList<FeatherNode> m_websocketNodes;
|
|
Networking *m_wowNet = nullptr; // wowlet: live remote-node feed from wownero.org/nodes
|
|
|
|
FeatherNode m_connection; // current active connection, if any
|
|
|
|
bool m_wsNodesReceived = false;
|
|
bool m_enableAutoconnect = true;
|
|
|
|
bool m_allowConnection = false;
|
|
|
|
FeatherNode pickEligibleNode();
|
|
void fetchRemoteNodeList(); // wowlet: pull /nodes/api/nodes/wallet, replacing the disabled websocket
|
|
|
|
bool useOnionNodes();
|
|
bool useI2PNodes();
|
|
bool useSocks5Proxy(const FeatherNode &node);
|
|
|
|
void resetLocalState();
|
|
void exhausted();
|
|
int modeHeight(const QList<FeatherNode> &nodes);
|
|
};
|
|
|
|
#endif //FEATHER_NODES_H
|