Browse Source

Mining tab

Add a mining tab for SilentDragonX. It will recognize if a node is already mining
and render the correct data. Currently the tab will only show up for DragonX.

The GUI could use some improvements but allows starting+stopping mining and choosing
how many threads to mine with. It also renders stats that many miners will find useful.
pull/112/head
Duke 1 year ago
parent
commit
32b08ff83a
  1. 4
      run-after-build-sdx.sh
  2. 126
      src/mainwindow.cpp
  3. 1
      src/mainwindow.h
  4. 155
      src/rpc.cpp
  5. 5
      src/rpc.h
  6. 5
      src/sd.h
  7. 2
      src/settings.h

4
run-after-build-sdx.sh

@ -0,0 +1,4 @@
#!/usr/bin/env bash
# Copyright 2019-2023 The Hush Developers
./build-sdx.sh && ./silentdragonx

126
src/mainwindow.cpp

@ -23,6 +23,8 @@
#include "connection.h"
#include "requestdialog.h"
#include "websockets.h"
#include <QLCDNumber>
#include "sd.h"
extern bool isdragonx;
@ -129,6 +131,8 @@ MainWindow::MainWindow(QWidget *parent) :
rpc = new RPC(this);
qDebug() << "Created RPC";
setupMiningTab();
restoreSavedStates();
if (AppDataServer::getInstance()->isAppConnected()) {
@ -1485,6 +1489,128 @@ QString peer2ip(QString peer) {
return ip;
}
void MainWindow::setupMiningTab() {
DEBUG("setting up mining tab");
//TODO: for other HSC's, look at getinfo.ac_algo == randomx
if(isdragonx) {
int hwc = std::thread::hardware_concurrency();
DEBUG("hardware concurrency = " << hwc);
auto tab = new QWidget();
tab->setObjectName(QString::fromUtf8("Mining"));
ui->tabWidget->addTab(tab, QString(tr("Mining")));
auto gridLayout = new QGridLayout(tab);
gridLayout->setSpacing(6);
//auto label1 = new QLabel(tr("Threads"), tab);
auto label2 = new QLabel(tr("Mining threads"), tab);
auto label3 = new QLabel(tr("Local Hashrate (hashes/sec)"), tab);
auto label4 = new QLabel(tr("Network Hashrate (hashes/sec)"), tab);
auto label5 = new QLabel(tr("Difficulty"), tab);
auto label6 = new QLabel(tr("Estimated Hours To Find A Block"), tab);
auto combo = new QComboBox(tab);
combo->setObjectName("genproclimit");
// give options from 1 to hwc/2 , which should represent physical CPUs
for(int i=0; i < hwc/2; i++) {
combo->insertItem(i, QString::number(i+1));
}
QFont font;
font.setBold(true);
font.setPointSize(18);
// probably a better way to do this but yolo
label2->setFont(font);
label3->setFont(font);
label4->setFont(font);
label5->setFont(font);
label6->setFont(font);
auto lcd1 = new QLCDNumber(8, tab);
auto lcd2 = new QLCDNumber(8, tab);
auto lcd3 = new QLCDNumber(8, tab);
auto lcd4 = new QLCDNumber(8, tab);
auto lcd5 = new QLCDNumber(8, tab);
lcd1->display(QString("0.0"));
lcd1->setObjectName("localhashrate");
lcd2->display(QString("0"));
lcd2->setObjectName("networkhashrate");
lcd3->display(QString("0.0"));
lcd3->setObjectName("difficulty");
lcd4->display(QString("-"));
lcd4->setObjectName("luck");
lcd5->display(QString("0"));
lcd5->setObjectName("miningthreads");
auto button1 = new QPushButton(tr("Start Mining"), tab);
auto button2 = new QPushButton(tr("Stop Mining"), tab);
button1->setFont(font);
button2->setFont(font);
button1->setObjectName("startmining");
button2->setObjectName("stopmining");
QObject::connect(button1, &QPushButton::clicked, [&] () {
DEBUG("START MINING");
int threads = 1;
auto combo = ui->tabWidget->findChild<QComboBox *>("genproclimit");
if(combo != nullptr) {
DEBUG("found combo with selection index=" << combo->currentIndex() );
threads = combo->currentIndex() + 1;
}
ui->statusBar->showMessage(tr("Starting mining with ") + QString::number(threads) + tr(" threads"), 5000);
rpc->setGenerate(threads, [=] (QJsonValue response){
DEBUG("setgenerate response=" << response);
// these values will auto-update in a few seconds but do it
// immediately so as to not confuse the user
// TODO: coredumps
// lcd5->display(QString(threads)); // miningthreads
});
});
QObject::connect(button2, &QPushButton::clicked, [&] () {
DEBUG("STOP MINING");
ui->statusBar->showMessage(tr("Stopping mining..."), 5000);
rpc->stopGenerate(0, [=] (QJsonValue response){
DEBUG("setgenerate response=" << response);
// these values will auto-update in a few seconds but do it
// immediately so as to not confuse the user
// TODO: coredumps
// lcd1->display(QString("0")); // localhash
// lcd4->display(QString("0")); // luck
// lcd5->display(QString("0")); // miningthreads
});
});
// both buttons disabled at creation time. when we know the current
// status of getmininginfo.generate we enable the correct button
button1->setEnabled(false);
button2->setEnabled(false);
// gridLayout->addWidget(radio, 0, 0);
// gridLayout->addWidget(label1, 0, 1, Qt::AlignLeft);
gridLayout->addWidget(button1, 0, 0);
//gridLayout->addWidget(label1, 0, 1);
gridLayout->addWidget(combo, 0, 1);
gridLayout->addWidget(button2, 1, 0);
gridLayout->addWidget(label2, 2, 0);
gridLayout->addWidget(lcd5, 2, 1);
gridLayout->addWidget(label3, 3, 0);
gridLayout->addWidget(lcd1, 3, 1);
gridLayout->addWidget(label4, 4, 0);
gridLayout->addWidget(lcd2, 4, 1);
gridLayout->addWidget(label5, 5, 0);
gridLayout->addWidget(lcd3, 5, 1);
gridLayout->addWidget(label6, 6, 0);
gridLayout->addWidget(lcd4, 6, 1);
} else {
// Mining tab currently only enabled for DragonX
}
}
void MainWindow::setupPeersTab() {
qDebug() << __FUNCTION__;
// Set up context menu on peers tab

1
src/mainwindow.h

@ -97,6 +97,7 @@ private:
void setupHushTab();
void setupChatTab();
void setupMarketTab();
void setupMiningTab();
void slot_change_theme(QString& themeName);
void slot_change_currency(const QString& currencyName);

155
src/rpc.cpp

@ -1,11 +1,13 @@
// Copyright 2019-2022 The Hush Developers
// Released under the GPLv3
#include <QLCDNumber>
#include "rpc.h"
#include "addressbook.h"
#include "settings.h"
#include "senttxstore.h"
#include "version.h"
#include "websockets.h"
#include "sd.h"
extern bool isdragonx;
@ -208,6 +210,16 @@ void RPC::getRescanInfo(const std::function<void(QJsonValue)>& cb){
conn->doRPCIgnoreError(makePayload(method), cb);
}
void RPC::getnetworksolps(const std::function<void(QJsonValue)>& cb){
QString method = "getnetworksolps";
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb);
}
void RPC::getlocalsolps(const std::function<void(QJsonValue)>& cb){
QString method = "getlocalsolps";
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb);
}
// get help
void RPC::help(const std::function<void(QJsonValue)>& cb){
QString method = "help";
@ -280,6 +292,28 @@ void RPC::newTaddr(const std::function<void(QJsonValue)>& cb) {
conn->doRPCWithDefaultErrorHandling(makePayload(method), cb);
}
void RPC::setGenerate(int proclimit, const std::function<void(QJsonValue)>& cb) {
QString method = "setgenerate";
QJsonObject payload = {
{"jsonrpc", "1.0"},
{"id", "42" },
{"method", method },
{"params", QJsonArray {true, proclimit}}
};
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::stopGenerate(int proclimit, const std::function<void(QJsonValue)>& cb) {
QString method = "setgenerate";
QJsonObject payload = {
{"jsonrpc", "1.0"},
{"id", "42" },
{"method", method },
{"params", QJsonArray {false, proclimit}}
};
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::getZViewKey(QString addr, const std::function<void(QJsonValue)>& cb) {
QString method = "z_exportviewingkey";
conn->doRPCWithDefaultErrorHandling(makePayload(method, addr), cb);
@ -776,6 +810,7 @@ void RPC::getInfoThenRefresh(bool force) {
ui->numconnections->setText(QString::number(connections) + " (" + QString::number(tlsconnections) + " TLS)" );
ui->tlssupport->setText(hasTLS ? "Yes" : "No");
/*
// Get network sol/s
QString method = "getnetworksolps";
conn->doRPCIgnoreError(makePayload(method), [=](const QJsonValue& reply) {
@ -787,6 +822,126 @@ void RPC::getInfoThenRefresh(bool force) {
} else {
ui->solrate->setText(QString::number((double)solrate / 1000000) % " MegaSol/s");
}
// find a QLCDNumber child of tabWidget named networkhashrate
auto hashrate = ui->tabWidget->findChild<QLCDNumber *>("networkhashrate");
qDebug() << "solrate=" << QString::number(solrate);
if(hashrate != nullptr) {
hashrate->display( QString::number( solrate, 'f', 2 ) );
} else {
qDebug() << "no widget named networkhashrate found";
}
// hashrate->display( QString::number( (double) solrate) );
});
// only look at local hashrate for dragonx
if(isdragonx) {
QString method = "getlocalsolps";
DEBUG("calling getlocalsolps");
conn->doRPCIgnoreError(makePayload(method), [=](const QJsonValue& reply) {
qDebug() << "reply=" << reply;
double solrate = reply.toDouble();
DEBUG("solrate=" % QString::number(solrate));
// find a QLCDNumber child of tabWidget named localhashrate
auto hashrate = ui->tabWidget->findChild<QLCDNumber *>("localhashrate");
if(hashrate != nullptr) {
hashrate->display( QString::number( (double) solrate, 'f', 2 ) );
} else {
qDebug() << "no widget named localhashrate found";
}
});
}
*/
// Get mining info
// This is better+faster than calling multiple RPCs such as getlocalsolps/getnetworksolps/getgenerate
// and getlocalsolps returns non-zero values even when not mining, which is not what we want
conn->doRPCIgnoreError(makePayload("getmininginfo"), [=](const QJsonValue& reply) {
QString localhashps = QString::number( reply["localhashps"].toDouble() );
QString networkhashps = QString::number( reply["networkhashps"].toDouble() );
QString generate = QString::number( reply["generate"].toBool() );
QString difficulty = QString::number( reply["difficulty"].toDouble() );
QString genproclimit = QString::number( reply["genproclimit"].toInt() );
if ( genproclimit == "-1" ) {
// Showing users they are mining with -1 threads by default is confusing
genproclimit = QString::number(0);
}
auto stopbutton = ui->tabWidget->findChild<QPushButton *>("stopmining");
auto startbutton = ui->tabWidget->findChild<QPushButton *>("startmining");
if(generate == "1") {
// already mining
stopbutton->setEnabled(true);
startbutton->setEnabled(false);
DEBUG("enabled stop mining button, disabled start mining button");
} else {
// not yet mining
startbutton->setEnabled(true);
stopbutton->setEnabled(false);
DEBUG("enabled start mining button, disabled stop mining button");
}
// Update network hashrate in "Node Info" tab
if(isdragonx) {
ui->solrate->setText(QString::number(networkhashps.toDouble()) % " Hash/s");
} else {
ui->solrate->setText(QString::number(networkhashps.toDouble() / 1000000) % " MegaSol/s");
}
// find a QLCDNumber child of tabWidget named localhashrate
auto localhashrate = ui->tabWidget->findChild<QLCDNumber *>("localhashrate");
if(localhashrate != nullptr) {
localhashrate->display( QString::number( localhashps.toDouble(), 'f', 2 ) );
} else {
qDebug() << "no widget named localhashrate found";
}
// find a QLCDNumber child of tabWidget named networkhashrate
auto nethashrate = ui->tabWidget->findChild<QLCDNumber *>("networkhashrate");
if(nethashrate != nullptr) {
nethashrate->display( QString::number( networkhashps.toInt() ) );
} else {
qDebug() << "no widget named networkhashrate found";
}
// find a QLCDNumber child of tabWidget named difficulty
auto diff = ui->tabWidget->findChild<QLCDNumber *>("difficulty");
if(nethashrate != nullptr) {
diff->display( QString::number( difficulty.toDouble(), 'f', 2 ) );
} else {
qDebug() << "no widget named difficulty found";
}
// find a QLCDNumber child of tabWidget named miningthreads
auto miningthreads = ui->tabWidget->findChild<QLCDNumber *>("miningthreads");
if(miningthreads != nullptr) {
miningthreads->display( QString::number( genproclimit.toInt() ) );
} else {
qDebug() << "no widget named difficulty found";
}
// find a QLCDNumber child of tabWidget named luck
auto luck = ui->tabWidget->findChild<QLCDNumber *>("luck");
if(luck != nullptr) {
if( generate == "0" ) {
// not mining, luck is not applicable
luck->display( QString("-") );
} else {
// luck = current estimate of time to find a block given current localhash and nethash
double percentOfNetHash = localhashps.toDouble() / networkhashps.toInt();
DEBUG( "% of nethash=" << percentOfNetHash );
if (percentOfNetHash > 0) {
//TODO: this is only for DRAGONX
int blocktime = 36;
double luckInSeconds = (1/percentOfNetHash)*blocktime;
double luckInHours = luckInSeconds / (60*60);
luck->display( QString::number( luckInHours , 'f', 2 ) );
}
}
} else {
qDebug() << "no widget named luck found";
}
});
// Get network info

5
src/rpc.h

@ -76,7 +76,6 @@ public:
void checkForUpdate(bool silent = true);
void refreshPrice();
void getZboardTopics(std::function<void(QMap<QString, QString>)> cb);
void executeTransaction(Tx tx,
const std::function<void(QString opid)> submitted,
@ -103,6 +102,8 @@ public:
void newZaddr(const std::function<void(QJsonValue)>& cb);
void newTaddr(const std::function<void(QJsonValue)>& cb);
void setGenerate(int proclimit, const std::function<void(QJsonValue)>& cb);
void stopGenerate(int proclimit, const std::function<void(QJsonValue)>& cb);
void getZPrivKey(QString addr, const std::function<void(QJsonValue)>& cb);
void getZViewKey(QString addr, const std::function<void(QJsonValue)>& cb);
void getTPrivKey(QString addr, const std::function<void(QJsonValue)>& cb);
@ -124,6 +125,8 @@ public:
void rescan(qint64 height, const std::function<void(QJsonValue)>& cb);
void getRescanInfo(const std::function<void(QJsonValue)>& cb);
void help(const std::function<void(QJsonValue)>& cb);
void getnetworksolps(const std::function<void(QJsonValue)>& cb);
void getlocalsolps(const std::function<void(QJsonValue)>& cb);
private:
void refreshBalances();

5
src/sd.h

@ -0,0 +1,5 @@
// Copyright 2019-2023 The Hush Developers
// Released under the GPLv3
#define DEBUG(x) (qDebug() << __func__ << ": " << x)

2
src/settings.h

@ -132,8 +132,6 @@ public:
static QString getDonationAddr();
static double getMinerFee();
static double getZboardAmount();
static QString getZboardAddr();
//TODO: this could be an advanced setting too
static int getMaxMobileAppTxns() { return 30; }

Loading…
Cancel
Save