mirror of
https://github.com/wownero/wowlet.git
synced 2026-10-17 15:21:30 +02:00
fix(mining): only offer to mine on a node that is ours
Start Mining was live regardless of which daemon the wallet was talking to. It is a `start_mining` RPC to the connected node, so on a remote one it is either refused — the button appearing to do nothing — or, if the operator left the RPC open, it sets someone else's machine mining to our address. Neither is a thing a wallet should offer. The axe now greys out unless `Nodes::connection().isLocal()`, hovering it explains why, and clicking anyway flashes the same notice rather than silently doing nothing. `onStartMining` refuses too: the QML disables the control, but a signal is not a promise. isLocal() counts localhost, .local, 127/8 and the RFC1918 ranges, so the embedded node and a daemon on the LAN both qualify. Claude-Session: https://claude.ai/code/session_01VLQ5DsgKv23osGANoFT3zF
This commit is contained in:
+1
-1
@@ -252,7 +252,7 @@ void MainWindow::initWidgets() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// [Mining] — wowlet: the pixel-art mining tab (mining.qml + embedded-node solo miner)
|
// [Mining] — wowlet: the pixel-art mining tab (mining.qml + embedded-node solo miner)
|
||||||
m_miningWidget = new MiningWidget(m_wallet, this);
|
m_miningWidget = new MiningWidget(m_wallet, m_nodes, this);
|
||||||
ui->tabWidget->addTab(m_miningWidget, "Mining");
|
ui->tabWidget->addTab(m_miningWidget, "Mining");
|
||||||
|
|
||||||
// [Notes]
|
// [Notes]
|
||||||
|
|||||||
@@ -512,6 +512,10 @@ Rectangle {
|
|||||||
Image {
|
Image {
|
||||||
id: imgAxe
|
id: imgAxe
|
||||||
visible: mining.daemonMiningState === 0
|
visible: mining.daemonMiningState === 0
|
||||||
|
// Greyed out while the wallet is on someone else's node:
|
||||||
|
// solo mining is an RPC to the daemon we are connected
|
||||||
|
// to, and that daemon has to be ours.
|
||||||
|
opacity: mining.canMine ? 1.0 : 0.35
|
||||||
source: "qrc:/mining/axe.png"
|
source: "qrc:/mining/axe.png"
|
||||||
width: 73
|
width: 73
|
||||||
height: 75
|
height: 75
|
||||||
@@ -551,7 +555,11 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(mining.daemonMiningState === 0) {
|
if (mining.daemonMiningState === 0) {
|
||||||
|
if (!mining.canMine) {
|
||||||
|
remoteNodeNotice.flash();
|
||||||
|
return;
|
||||||
|
}
|
||||||
root.startMining();
|
root.startMining();
|
||||||
root.calcAvailableHeightConsoleLines();
|
root.calcAvailableHeightConsoleLines();
|
||||||
}
|
}
|
||||||
@@ -559,14 +567,56 @@ Rectangle {
|
|||||||
root.stopMining();
|
root.stopMining();
|
||||||
}
|
}
|
||||||
onEntered: {
|
onEntered: {
|
||||||
imgAxe.height = 64
|
if (mining.canMine) {
|
||||||
imgAxe.width = 64
|
imgAxe.height = 64
|
||||||
|
imgAxe.width = 64
|
||||||
|
} else {
|
||||||
|
remoteNodeNotice.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onExited: {
|
onExited: {
|
||||||
imgAxe.height = 75
|
imgAxe.height = 75
|
||||||
imgAxe.width = 73
|
imgAxe.width = 73
|
||||||
|
remoteNodeNotice.hideSoon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Why the axe is greyed out. Shown on hover, and flashed if
|
||||||
|
// the axe is clicked anyway.
|
||||||
|
Rectangle {
|
||||||
|
id: remoteNodeNotice
|
||||||
|
z: parent.z + 5
|
||||||
|
visible: opacity > 0.01
|
||||||
|
opacity: 0.0
|
||||||
|
width: 250
|
||||||
|
height: noticeText.implicitHeight + 16
|
||||||
|
radius: 6
|
||||||
|
color: "#231a10"
|
||||||
|
border.color: "#e0b062"
|
||||||
|
border.width: 1
|
||||||
|
anchors.right: parent.left
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: noticeText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - 16
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: "#f2dcb4"
|
||||||
|
font.pointSize: 9
|
||||||
|
text: "Mining needs your own node.\nYou are connected to a remote one — " +
|
||||||
|
"start the built-in node or point the wallet at a local daemon " +
|
||||||
|
"under Settings to mine."
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity { NumberAnimation { duration: 160 } }
|
||||||
|
Timer { id: noticeHide; interval: 2600; onTriggered: remoteNodeNotice.opacity = 0.0 }
|
||||||
|
function show() { noticeHide.stop(); opacity = 1.0 }
|
||||||
|
function hideSoon() { noticeHide.restart() }
|
||||||
|
function flash() { show(); noticeHide.restart() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "MiningWidget.h"
|
#include "MiningWidget.h"
|
||||||
|
|
||||||
|
#include "utils/nodes.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
@@ -20,9 +22,10 @@ static QString formatUptime(qint64 secs) {
|
|||||||
.arg(secs % 60, 2, 10, QChar('0'));
|
.arg(secs % 60, 2, 10, QChar('0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
MiningWidget::MiningWidget(Wallet *wallet, QWidget *parent)
|
MiningWidget::MiningWidget(Wallet *wallet, Nodes *nodes, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_wallet(wallet)
|
, m_wallet(wallet)
|
||||||
|
, m_nodes(nodes)
|
||||||
, m_quickWidget(new QQuickWidget(this))
|
, m_quickWidget(new QQuickWidget(this))
|
||||||
, m_backend(new MiningBackend(this))
|
, m_backend(new MiningBackend(this))
|
||||||
, m_timer(new QTimer(this))
|
, m_timer(new QTimer(this))
|
||||||
@@ -46,6 +49,10 @@ MiningWidget::MiningWidget(Wallet *wallet, QWidget *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MiningWidget::onStartMining() {
|
void MiningWidget::onStartMining() {
|
||||||
|
// The QML disables the control, but the signal is not a promise: refuse
|
||||||
|
// here too rather than send start_mining to a stranger's daemon.
|
||||||
|
if (!m_backend->canMine())
|
||||||
|
return;
|
||||||
int threads = std::max(1, QThread::idealThreadCount() / 2);
|
int threads = std::max(1, QThread::idealThreadCount() / 2);
|
||||||
if (m_wallet->startMining(threads))
|
if (m_wallet->startMining(threads))
|
||||||
m_backend->setState(3);
|
m_backend->setState(3);
|
||||||
@@ -57,6 +64,8 @@ void MiningWidget::onStopMining() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MiningWidget::poll() {
|
void MiningWidget::poll() {
|
||||||
|
m_backend->setCanMine(m_nodes && m_nodes->connection().isLocal());
|
||||||
|
|
||||||
bool mining = m_wallet->isMining();
|
bool mining = m_wallet->isMining();
|
||||||
m_backend->setState(mining ? 3 : 0);
|
m_backend->setState(mining ? 3 : 0);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#define WOWLET_MININGWIDGET_H
|
#define WOWLET_MININGWIDGET_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
class Nodes;
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QQuickWidget>
|
#include <QQuickWidget>
|
||||||
|
|
||||||
@@ -16,16 +18,24 @@ class MiningBackend : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int daemonMiningState READ daemonMiningState NOTIFY daemonMiningStateChanged)
|
Q_PROPERTY(int daemonMiningState READ daemonMiningState NOTIFY daemonMiningStateChanged)
|
||||||
Q_PROPERTY(bool reducedMotion READ reducedMotion NOTIFY reducedMotionChanged)
|
Q_PROPERTY(bool reducedMotion READ reducedMotion NOTIFY reducedMotionChanged)
|
||||||
|
Q_PROPERTY(bool canMine READ canMine NOTIFY canMineChanged)
|
||||||
public:
|
public:
|
||||||
explicit MiningBackend(QObject *parent = nullptr) : QObject(parent) {}
|
explicit MiningBackend(QObject *parent = nullptr) : QObject(parent) {}
|
||||||
int daemonMiningState() const { return m_state; }
|
int daemonMiningState() const { return m_state; }
|
||||||
void setState(int s) { if (m_state != s) { m_state = s; emit daemonMiningStateChanged(); } }
|
void setState(int s) { if (m_state != s) { m_state = s; emit daemonMiningStateChanged(); } }
|
||||||
bool reducedMotion() const { return m_reducedMotion; }
|
bool reducedMotion() const { return m_reducedMotion; }
|
||||||
void setReducedMotion(bool r) { if (m_reducedMotion != r) { m_reducedMotion = r; emit reducedMotionChanged(); } }
|
void setReducedMotion(bool r) { if (m_reducedMotion != r) { m_reducedMotion = r; emit reducedMotionChanged(); } }
|
||||||
|
// Solo mining is a start_mining RPC to whichever daemon the wallet is
|
||||||
|
// connected to. Against a remote node that is either refused outright or —
|
||||||
|
// worse, if the operator left the RPC open — it puts someone else's machine
|
||||||
|
// to work on our address. It is only ever ours to ask of our own node.
|
||||||
|
bool canMine() const { return m_canMine; }
|
||||||
|
void setCanMine(bool c) { if (m_canMine != c) { m_canMine = c; emit canMineChanged(); } }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void daemonMiningStateChanged();
|
void daemonMiningStateChanged();
|
||||||
void reducedMotionChanged();
|
void reducedMotionChanged();
|
||||||
|
void canMineChanged();
|
||||||
void daemonOutput(const QString &line);
|
void daemonOutput(const QString &line);
|
||||||
void syncStatus(int from, int to, int pct);
|
void syncStatus(int from, int to, int pct);
|
||||||
void uptimeChanged(const QString &uptime);
|
void uptimeChanged(const QString &uptime);
|
||||||
@@ -34,6 +44,7 @@ signals:
|
|||||||
private:
|
private:
|
||||||
int m_state = 0; // 0:idle 1:startup 2:syncing 3:mining
|
int m_state = 0; // 0:idle 1:startup 2:syncing 3:mining
|
||||||
bool m_reducedMotion = false;
|
bool m_reducedMotion = false;
|
||||||
|
bool m_canMine = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wowlet: the pixel-art mining tab. Hosts mining.qml and wires its pick-axe to the wallet's solo
|
// wowlet: the pixel-art mining tab. Hosts mining.qml and wires its pick-axe to the wallet's solo
|
||||||
@@ -41,7 +52,7 @@ private:
|
|||||||
class MiningWidget : public QWidget {
|
class MiningWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit MiningWidget(Wallet *wallet, QWidget *parent = nullptr);
|
explicit MiningWidget(Wallet *wallet, Nodes *nodes, QWidget *parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onStartMining();
|
void onStartMining();
|
||||||
@@ -50,6 +61,7 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Wallet *m_wallet;
|
Wallet *m_wallet;
|
||||||
|
Nodes *m_nodes;
|
||||||
QQuickWidget *m_quickWidget;
|
QQuickWidget *m_quickWidget;
|
||||||
MiningBackend *m_backend;
|
MiningBackend *m_backend;
|
||||||
QTimer *m_timer;
|
QTimer *m_timer;
|
||||||
|
|||||||
Reference in New Issue
Block a user