diff --git a/run-after-build-sdx.sh b/run-after-build-sdx.sh new file mode 100755 index 0000000..0055a24 --- /dev/null +++ b/run-after-build-sdx.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Copyright 2019-2023 The Hush Developers + +./build-sdx.sh && ./silentdragonx diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 021e1cd..ce05065 100755 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -23,6 +23,8 @@ #include "connection.h" #include "requestdialog.h" #include "websockets.h" +#include +#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("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 diff --git a/src/mainwindow.h b/src/mainwindow.h index 0670d85..bfd187a 100644 --- a/src/mainwindow.h +++ b/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); diff --git a/src/rpc.cpp b/src/rpc.cpp index 42c0117..e9f4312 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -1,11 +1,13 @@ // Copyright 2019-2022 The Hush Developers // Released under the GPLv3 +#include #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& cb){ conn->doRPCIgnoreError(makePayload(method), cb); } +void RPC::getnetworksolps(const std::function& cb){ + QString method = "getnetworksolps"; + conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); +} + +void RPC::getlocalsolps(const std::function& cb){ + QString method = "getlocalsolps"; + conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); +} + // get help void RPC::help(const std::function& cb){ QString method = "help"; @@ -280,6 +292,28 @@ void RPC::newTaddr(const std::function& cb) { conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } +void RPC::setGenerate(int proclimit, const std::function& 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& 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& 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("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("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("stopmining"); + auto startbutton = ui->tabWidget->findChild("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("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("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("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("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("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 diff --git a/src/rpc.h b/src/rpc.h index 85daba5..2b4efc1 100755 --- a/src/rpc.h +++ b/src/rpc.h @@ -76,7 +76,6 @@ public: void checkForUpdate(bool silent = true); void refreshPrice(); - void getZboardTopics(std::function)> cb); void executeTransaction(Tx tx, const std::function submitted, @@ -103,6 +102,8 @@ public: void newZaddr(const std::function& cb); void newTaddr(const std::function& cb); + void setGenerate(int proclimit, const std::function& cb); + void stopGenerate(int proclimit, const std::function& cb); void getZPrivKey(QString addr, const std::function& cb); void getZViewKey(QString addr, const std::function& cb); void getTPrivKey(QString addr, const std::function& cb); @@ -124,6 +125,8 @@ public: void rescan(qint64 height, const std::function& cb); void getRescanInfo(const std::function& cb); void help(const std::function& cb); + void getnetworksolps(const std::function& cb); + void getlocalsolps(const std::function& cb); private: void refreshBalances(); diff --git a/src/sd.h b/src/sd.h new file mode 100644 index 0000000..418f9b4 --- /dev/null +++ b/src/sd.h @@ -0,0 +1,5 @@ +// Copyright 2019-2023 The Hush Developers +// Released under the GPLv3 + +#define DEBUG(x) (qDebug() << __func__ << ": " << x) + diff --git a/src/settings.h b/src/settings.h index a426805..cd22b0f 100644 --- a/src/settings.h +++ b/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; }