mirror of
https://git.wownero.com/wowlet/wowlet.git
synced 2024-08-15 01:03:14 +00:00
190 lines
6.8 KiB
C++
190 lines
6.8 KiB
C++
// SPDX-License-Identifier: BSD-3-Clause
|
|
// Copyright (c) 2020-2021, The Monero Project.
|
|
|
|
#include <QScreen>
|
|
#include <QDesktopWidget>
|
|
#include <QDesktopServices>
|
|
|
|
#include "utils/utils.h"
|
|
#include "utils/xmrig.h"
|
|
#include "mainwindow.h"
|
|
|
|
XmRig::XmRig(const QString &configDir, QObject *parent) :
|
|
QObject(parent),
|
|
m_statusTimer(new QTimer(this))
|
|
{
|
|
m_statusTimer->setInterval(5000);
|
|
connect(m_statusTimer, &QTimer::timeout, [this]{
|
|
if(daemonMiningState == DaemonMiningState::mining && m_process.state() == QProcess::Running)
|
|
m_process.write("status\n");
|
|
});
|
|
}
|
|
|
|
void XmRig::prepare() {
|
|
m_process.setProcessChannelMode(QProcess::MergedChannels);
|
|
connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::onHandleProcessOutput);
|
|
connect(&m_process, &QProcess::errorOccurred, this, &XmRig::onHandleProcessError);
|
|
connect(&m_process, &QProcess::stateChanged, this, &XmRig::onProcessStateChanged);
|
|
}
|
|
|
|
void XmRig::stop() {
|
|
if(m_process.state() == QProcess::Running) {
|
|
#if defined(Q_OS_WIN)
|
|
m_process.kill(); // https://doc.qt.io/qt-5/qprocess.html#terminate
|
|
#else
|
|
m_process.terminate();
|
|
#endif
|
|
}
|
|
m_statusTimer->stop();
|
|
}
|
|
|
|
bool XmRig::start(const QString &path, int threads) {
|
|
m_ctx = MainWindow::getContext();
|
|
|
|
auto state = m_process.state();
|
|
if (state == QProcess::ProcessState::Running ||
|
|
state == QProcess::ProcessState::Starting ||
|
|
daemonMiningState != DaemonMiningState::idle) {
|
|
emit error("Can't start wownerod, already running or starting");
|
|
return false;
|
|
}
|
|
|
|
if(path.isEmpty()) {
|
|
emit error("wownerod path seems to be empty. Go to the mining settings tab and point towards the wownerod executable!");
|
|
return false;
|
|
}
|
|
|
|
if(!Utils::fileExists(path)) {
|
|
emit error(QString("Path to wownerod binary is invalid; file does not exist: %1").arg(path));
|
|
return false;
|
|
}
|
|
|
|
auto privateSpendKey = m_ctx->currentWallet->getSecretSpendKey();
|
|
QStringList arguments;
|
|
|
|
// dont exit when binding fails - we dont need
|
|
// to bind to any ports when we just want to mine
|
|
arguments << "--no-zmq";
|
|
arguments << "--rpc-ignore-ipv4";
|
|
arguments << "--p2p-ignore-ipv4";
|
|
|
|
arguments << "--mining-threads" << QString::number(threads);
|
|
arguments << "--start-mining" << m_ctx->currentWallet->address(0, 0);
|
|
arguments << "--spendkey" << privateSpendKey;
|
|
|
|
QString cmd = QString("%1 %2").arg(path, arguments.join(" "));
|
|
cmd = cmd.replace(privateSpendKey, "[redacted]");
|
|
emit output(cmd.toUtf8());
|
|
|
|
m_process.start(path, arguments);
|
|
m_statusTimer->start();
|
|
return true;
|
|
}
|
|
|
|
void XmRig::onProcessStateChanged(QProcess::ProcessState state) {
|
|
if(state == QProcess::ProcessState::Running) {
|
|
emit output("wownerod started");
|
|
changeDaemonState(DaemonMiningState::startup);
|
|
}
|
|
else if (state == QProcess::ProcessState::NotRunning) {
|
|
emit output("wownerod stopped");
|
|
changeDaemonState(DaemonMiningState::idle);
|
|
}
|
|
}
|
|
|
|
void XmRig::onHandleProcessOutput() {
|
|
QByteArray data = m_process.readAllStandardOutput();
|
|
|
|
for(auto &line: data.split('\n')) {
|
|
// remove timestamp
|
|
if(line.indexOf("\tI") >= 20)
|
|
line = line.remove(0, line.indexOf("\tI") + 2);
|
|
line = line.trimmed();
|
|
|
|
// sad attempt at removing ANSI color codes
|
|
// yes this is stupid, no i dont care
|
|
// works remarkably well so far lmao
|
|
auto ansi_start = QByteArray("\x1b\x5b");
|
|
line = line.replace(ansi_start, "");
|
|
line = line.replace("0;36m", "");
|
|
line = line.replace("0;35m", "");
|
|
line = line.replace("0;34m", "");
|
|
line = line.replace("0;33m", "");
|
|
line = line.replace("0;32m", "");
|
|
line = line.replace("1;32m", "");
|
|
line = line.replace("1;33m", "");
|
|
line = line.replace("1;34m", "");
|
|
line = line.replace("1;35m", "");
|
|
line = line.replace("1;36m", "");
|
|
if(line.startsWith("0m")) continue;
|
|
|
|
auto lower = line.toLower();
|
|
if(lower.isEmpty() || lower.startsWith("status")) continue;
|
|
|
|
// skip ascii/ansi art
|
|
unsigned int printable_chars_pct = 100 * Utils::countAlphaNum(lower) / lower.length();
|
|
if(printable_chars_pct < 60) continue;
|
|
|
|
if(lower.startsWith("the daemon will start synchronizing")) {
|
|
changeDaemonState(DaemonMiningState::startup);
|
|
} else if(lower.startsWith("synchronization started")) {
|
|
changeDaemonState(DaemonMiningState::syncing);
|
|
} else if(lower.startsWith("synced") && lower.contains("left")) {
|
|
if(daemonMiningState < DaemonMiningState::syncing) changeDaemonState(DaemonMiningState::syncing);
|
|
QRegularExpression re("synced (\\d+)\\/(\\d+) \\((\\d+)%, (\\d+) left");
|
|
|
|
QRegularExpressionMatch match = re.match(lower);
|
|
if (match.hasMatch()) {
|
|
auto from = match.captured(1);
|
|
auto to = match.captured(2);
|
|
auto pct = match.captured(3);
|
|
m_from = from.toUInt();
|
|
m_to = to.toUInt();
|
|
emit syncStatus(m_from, m_to, pct.toInt());
|
|
}
|
|
} else if(lower.contains("mining started. good luck")) {
|
|
emit syncStatus(m_to, m_to, 100);
|
|
changeDaemonState(DaemonMiningState::mining);
|
|
}
|
|
else if(lower.contains("you won a block reward"))
|
|
emit blockReward();
|
|
else if(lower.contains("mining at")) {
|
|
QRegularExpression re("Height\\: (\\d+)\\/(\\d+) \\((.*)\\) on mainnet, mining at (.*), net hash .*, uptime (.*)");
|
|
|
|
QRegularExpressionMatch match = re.match(line);
|
|
if (match.hasMatch()) {
|
|
m_from = match.captured(1).toUInt();
|
|
m_to = match.captured(2).toUInt();
|
|
unsigned int pct = match.captured(3).replace("%", "").toDouble();
|
|
auto rate = match.captured(4);
|
|
auto uptime = match.captured(5).replace(" ", "");
|
|
|
|
emit uptimeChanged(uptime);
|
|
emit syncStatus(m_to, m_to, pct);
|
|
emit hashrate(rate);
|
|
|
|
line = line.remove(0, line.indexOf("mining at"));
|
|
}
|
|
}
|
|
|
|
emit output(line.trimmed());
|
|
}
|
|
}
|
|
|
|
void XmRig::changeDaemonState(const DaemonMiningState state) {
|
|
if(daemonMiningState == state) return;
|
|
|
|
daemonMiningState = state;
|
|
emit daemonStateChanged(daemonMiningState);
|
|
}
|
|
|
|
void XmRig::onHandleProcessError(QProcess::ProcessError err) {
|
|
if (err == QProcess::ProcessError::Crashed)
|
|
emit error("wownerod crashed or killed");
|
|
else if (err == QProcess::ProcessError::FailedToStart) {
|
|
auto path = config()->get(Config::wownerodPath).toString();
|
|
emit error(QString("wownerod binary failed to start: %1").arg(path));
|
|
}
|
|
|
|
changeDaemonState(DaemonMiningState::idle);
|
|
}
|