From a0eb012ec9deb4978f8f5a5070e45dbe5ae96248 Mon Sep 17 00:00:00 2001 From: D Date: Tue, 15 Aug 2023 03:16:16 -0500 Subject: [PATCH] Added dynamic variables to support building for any HSC --- application-sdx.qrc | 1 + application.qrc | 1 + res/css/tumin.css | 114 +++ src/3rdparty/qrcode/QrCode.cpp | 4 +- src/3rdparty/qrcode/QrCode.hpp | 2 +- src/addressbook.cpp | 208 +++--- src/connection.cpp | 610 ++++++++++------ src/main.cpp | 188 +++-- src/mainwindow.cpp | 895 +++++++++++++---------- src/requestdialog.cpp | 76 +- src/rpc.cpp | 1250 ++++++++++++++++++-------------- src/senttxstore.cpp | 99 ++- src/settings.cpp | 396 ++++++---- src/txtablemodel.cpp | 288 +++++--- 14 files changed, 2465 insertions(+), 1667 deletions(-) create mode 100644 res/css/tumin.css diff --git a/application-sdx.qrc b/application-sdx.qrc index 035b6a0..514957d 100644 --- a/application-sdx.qrc +++ b/application-sdx.qrc @@ -59,6 +59,7 @@ res/css/light.css res/css/midnight.css res/css/dragonx.css + res/css/tumin.css res/images/blue/unchecked.png diff --git a/application.qrc b/application.qrc index f6867ed..7054fe5 100644 --- a/application.qrc +++ b/application.qrc @@ -59,6 +59,7 @@ res/css/light.css res/css/midnight.css res/css/dragonx.css + res/css/tumin.css res/images/blue/unchecked.png diff --git a/res/css/tumin.css b/res/css/tumin.css new file mode 100644 index 0000000..99ba5dc --- /dev/null +++ b/res/css/tumin.css @@ -0,0 +1,114 @@ +QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow { + background-color: #232834; + color: #91a4b8; +} +QTabWidget QTabBar::tab { + padding-left:20px; + padding-right:20px; + padding-top:5px; + padding-bottom:5px; + border: 1px solid #343F4B; + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); +} +QTabWidget QTabBar::tab:selected { + min-height: 10px; + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); + color:#91a4b8; + border: 1px ridge #91a4b8; +} +QTabWidget QTabBar::tab:hover { + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); + color:#91a4b8; + border: 1px ridge #91a4b8; + min-height: 20px +} +QHeaderView { + /* Table Header */ + background-color:#232834; +} +QHeaderView::section { + /* Table Header Sections */ + qproperty-alignment:center; + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); + color:#91a4b8; + min-height:25px; + font-weight:bold; + font-size:11px; + outline:0; + border:1px solid #343F4B; + border-right:1px solid #91a4b8; + border-left:1px solid #91a4b8; + padding-left:5px; + padding-right:5px; + padding-top:2px; + padding-bottom:2px; +} +QHeaderView::section:last { + border-right: 0px solid #d7d7d7; +} +QScrollArea { + background:transparent; + border:0px; +} +QTableView { + /* Table - has to be selected as a class otherwise it throws off QCalendarWidget */ + background:#232834; +} +QTableView::item { + /* Table Item */ + background-color:#232834; + border:1px solid #91a4b8; + font-size:12px; +} +QTableView::item:selected { + /* Table Item Selected */ + background-color:#91a4b8; + color:#232834; +} +QMenuBar { + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); + color: #91a4b8; +} +QMenuBar::item { + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); + color: #91a4b8; +} +QMenuBar::item:selected { + background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #343F4B, stop: 1 #232834); +} +QPushButton#startmining { + background-color: #343F4B; + border-color: #91A4B8; + padding: 10px; +} +QPushButton#startmining:hover { + background-color: #232834; +} +QPushButton#startmining:pressed { + background-color: #232834; +} +QPushButton#startmining:disabled { + background-color: #232834; +} +QPushButton#stopmining { + background-color: #343F4B; + border-color: #91A4B8; + padding: 10px; + margin-top: 7px; +} +QPushButton#stopmining:hover { + background-color: #232834; +} +QPushButton#stopmining:pressed { + background-color: #232834; +} +QPushButton#stopmining:disabled { + background-color: #232834; +} +QComboBox#genproclimit{ + font-size: 24px; + height: 40px; +} +QMenu::item:selected{ + background-color: #343F4B +} diff --git a/src/3rdparty/qrcode/QrCode.cpp b/src/3rdparty/qrcode/QrCode.cpp index 0a8b12f..d394a81 100644 --- a/src/3rdparty/qrcode/QrCode.cpp +++ b/src/3rdparty/qrcode/QrCode.cpp @@ -166,7 +166,7 @@ bool QrCode::getModule(int x, int y) const { } -std::string QrCode::toSvgString(int border) const { +QString QrCode::toSvgString(int border) const { if (border < 0) throw std::domain_error("Border must be non-negative"); if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) @@ -190,7 +190,7 @@ std::string QrCode::toSvgString(int border) const { } sb << "\" fill=\"#000000\"/>\n"; sb << "\n"; - return sb.str(); + return QString::fromStdString(sb.str()); } diff --git a/src/3rdparty/qrcode/QrCode.hpp b/src/3rdparty/qrcode/QrCode.hpp index 1904844..9aec236 100644 --- a/src/3rdparty/qrcode/QrCode.hpp +++ b/src/3rdparty/qrcode/QrCode.hpp @@ -187,7 +187,7 @@ class QrCode final { * Returns a string of SVG code for an image depicting this QR Code, with the given number * of border modules. The string always uses Unix newlines (\n), regardless of the platform. */ - public: std::string toSvgString(int border) const; + public: QString toSvgString(int border) const; diff --git a/src/addressbook.cpp b/src/addressbook.cpp index c61e302..01f15d6 100644 --- a/src/addressbook.cpp +++ b/src/addressbook.cpp @@ -7,96 +7,110 @@ #include "mainwindow.h" #include "rpc.h" -extern bool isdragonx; +extern bool isHSC; +extern std::string HSC_name; AddressBookModel::AddressBookModel(QTableView *parent) - : QAbstractTableModel(parent) { + : QAbstractTableModel(parent) +{ headers << tr("Label") << tr("Address"); this->parent = parent; loadData(); } -AddressBookModel::~AddressBookModel() { +AddressBookModel::~AddressBookModel() +{ saveData(); } -void AddressBookModel::saveData() { +void AddressBookModel::saveData() +{ // Save column positions QSettings().setValue("addresstablegeometry", parent->horizontalHeader()->saveState()); } - -void AddressBookModel::loadData() { +void AddressBookModel::loadData() +{ labels = AddressBook::getInstance()->getAllAddressLabels(); parent->horizontalHeader()->restoreState(QSettings().value("addresstablegeometry").toByteArray()); } -void AddressBookModel::addNewLabel(QString label, QString addr) { - //labels.push_back(QPair(label, addr)); +void AddressBookModel::addNewLabel(QString label, QString addr) +{ + // labels.push_back(QPair(label, addr)); AddressBook::getInstance()->addAddressLabel(label, addr); labels.clear(); labels = AddressBook::getInstance()->getAllAddressLabels(); - dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); + dataChanged(index(0, 0), index(labels.size() - 1, columnCount(index(0, 0)) - 1)); layoutChanged(); } -void AddressBookModel::removeItemAt(int row) { +void AddressBookModel::removeItemAt(int row) +{ if (row >= labels.size()) return; AddressBook::getInstance()->removeAddressLabel(labels[row].first, labels[row].second); - + labels.clear(); labels = AddressBook::getInstance()->getAllAddressLabels(); - dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); + dataChanged(index(0, 0), index(labels.size() - 1, columnCount(index(0, 0)) - 1)); layoutChanged(); } -QPair AddressBookModel::itemAt(int row) { - if (row >= labels.size()) return QPair(); +QPair AddressBookModel::itemAt(int row) +{ + if (row >= labels.size()) + return QPair(); return labels.at(row); } - -int AddressBookModel::rowCount(const QModelIndex&) const { +int AddressBookModel::rowCount(const QModelIndex &) const +{ return labels.size(); } -int AddressBookModel::columnCount(const QModelIndex&) const { +int AddressBookModel::columnCount(const QModelIndex &) const +{ return headers.size(); } - -QVariant AddressBookModel::data(const QModelIndex &index, int role) const { - if (role == Qt::DisplayRole) { - switch(index.column()) { - case 0: return labels.at(index.row()).first; - case 1: return labels.at(index.row()).second; +QVariant AddressBookModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case 0: + return labels.at(index.row()).first; + case 1: + return labels.at(index.row()).second; } } return QVariant(); -} - +} -QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { +QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { return headers.at(section); } return QVariant(); } - //=============== // AddressBook //=============== -void AddressBook::open(MainWindow* parent, QLineEdit* target) { +void AddressBook::open(MainWindow *parent, QLineEdit *target) +{ QDialog d(parent); Ui_addressBook ab; ab.setupUi(&d); @@ -109,21 +123,25 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { ab.addresses->setModel(&model); // If there is no target, the we'll call the button "Ok", else "Pick" - if (target != nullptr) { + if (target != nullptr) + { ab.buttonBox->button(QDialogButtonBox::Ok)->setText(QObject::tr("Pick")); - } + } // Connect the dialog's closing to updating the label address completor - QObject::connect(&d, &QDialog::finished, [=] (auto) { parent->updateLabels(); }); + QObject::connect(&d, &QDialog::finished, [=](auto) + { parent->updateLabels(); }); // If there is a target then make it the addr for the "Add to" button - if (target != nullptr && Settings::isValidAddress(target->text())) { + if (target != nullptr && Settings::isValidAddress(target->text())) + { ab.addr->setText(target->text()); ab.label->setFocus(); } // Add new address button - QObject::connect(ab.addNew, &QPushButton::clicked, [&] () { + QObject::connect(ab.addNew, &QPushButton::clicked, [&]() + { auto addr = ab.addr->text().trimmed(); QString newLabel = ab.label->text(); @@ -150,11 +168,11 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { return; } - model.addNewLabel(newLabel, ab.addr->text()); - }); + model.addNewLabel(newLabel, ab.addr->text()); }); // Import Button - QObject::connect(ab.btnImport, &QPushButton::clicked, [&] () { + QObject::connect(ab.btnImport, &QPushButton::clicked, [&]() + { // Get the import file name. auto fileName = QFileDialog::getOpenFileUrl(&d, QObject::tr("Import Address Book"), QUrl(), "CSV file (*.csv)"); @@ -184,15 +202,16 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { } QMessageBox::information(&d, QObject::tr("Address Book Import Done"), - QObject::tr("Imported %1 new Address book entries").arg(numImported)); - }); + QObject::tr("Imported %1 new Address book entries").arg(numImported)); }); - auto fnSetTargetLabelAddr = [=] (QLineEdit* target, QString label, QString addr) { + auto fnSetTargetLabelAddr = [=](QLineEdit *target, QString label, QString addr) + { target->setText(label % "/" % addr); }; // Double-Click picks the item - QObject::connect(ab.addresses, &QTableView::doubleClicked, [&] (auto index) { + QObject::connect(ab.addresses, &QTableView::doubleClicked, [&](auto index) + { // If there's no target, then double-clicking does nothing. if (!target) return; @@ -202,12 +221,12 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { QString lbl = model.itemAt(index.row()).first; QString addr = model.itemAt(index.row()).second; d.accept(); - fnSetTargetLabelAddr(target, lbl, addr); - }); + fnSetTargetLabelAddr(target, lbl, addr); }); // Right-Click ab.addresses->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(ab.addresses, &QTableView::customContextMenuRequested, [&] (QPoint pos) { + QObject::connect(ab.addresses, &QTableView::customContextMenuRequested, [&](QPoint pos) + { QModelIndex index = ab.addresses->indexAt(pos); if (index.row() < 0) return; @@ -233,12 +252,13 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { model.removeItemAt(index.row()); }); - menu.exec(ab.addresses->viewport()->mapToGlobal(pos)); - }); + menu.exec(ab.addresses->viewport()->mapToGlobal(pos)); }); - if (d.exec() == QDialog::Accepted && target != nullptr) { + if (d.exec() == QDialog::Accepted && target != nullptr) + { auto selection = ab.addresses->selectionModel(); - if (selection && selection->hasSelection() && selection->selectedRows().size() > 0) { + if (selection && selection->hasSelection() && selection->selectedRows().size() > 0) + { auto item = model.itemAt(selection->selectedRows().at(0).row()); fnSetTargetLabelAddr(target, item.first, item.second); } @@ -251,26 +271,30 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { //============= // AddressBook singleton class //============= -AddressBook* AddressBook::getInstance() { +AddressBook *AddressBook::getInstance() +{ if (!instance) instance = new AddressBook(); return instance; } -AddressBook::AddressBook() { +AddressBook::AddressBook() +{ readFromStorage(); } -void AddressBook::readFromStorage() { +void AddressBook::readFromStorage() +{ QFile file(AddressBook::writeableFile()); - if (file.exists()) { + if (file.exists()) + { allLabels.clear(); file.open(QIODevice::ReadOnly); - QDataStream in(&file); // read the data serialized from the file + QDataStream in(&file); // read the data serialized from the file QString version; - in >> version >> allLabels; + in >> version >> allLabels; file.close(); } @@ -284,40 +308,48 @@ void AddressBook::readFromStorage() { // } } -void AddressBook::writeToStorage() { +void AddressBook::writeToStorage() +{ QFile file(AddressBook::writeableFile()); file.open(QIODevice::ReadWrite | QIODevice::Truncate); - QDataStream out(&file); // we will serialize the data into the file + QDataStream out(&file); // we will serialize the data into the file out << QString("v1") << allLabels; file.close(); } -QString AddressBook::writeableFile() { +QString AddressBook::writeableFile() +{ auto filename = QStringLiteral("addresslabels.dat"); - if (isdragonx) { - filename = QStringLiteral("addresslabels-drgx.dat"); + if (isHSC) + { + filename = QString::fromStdString("addresslabels-" + HSC_name + ".dat"); } auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); if (!dir.exists()) QDir().mkpath(dir.absolutePath()); - if (Settings::getInstance()->isTestnet()) { + if (Settings::getInstance()->isTestnet()) + { return dir.filePath("testnet-" % filename); - } else { + } + else + { return dir.filePath(filename); } } - // Add a new address/label to the database -void AddressBook::addAddressLabel(QString label, QString address) { +void AddressBook::addAddressLabel(QString label, QString address) +{ Q_ASSERT(Settings::isValidAddress(address)); // First, remove any existing label // Iterate over the list and remove the label/address - for (int i=0; i < allLabels.size(); i++) { - if (allLabels[i].first == label) { + for (int i = 0; i < allLabels.size(); i++) + { + if (allLabels[i].first == label) + { removeAddressLabel(allLabels[i].first, allLabels[i].second); } } @@ -327,10 +359,13 @@ void AddressBook::addAddressLabel(QString label, QString address) { } // Remove a new address/label from the database -void AddressBook::removeAddressLabel(QString label, QString address) { +void AddressBook::removeAddressLabel(QString label, QString address) +{ // Iterate over the list and remove the label/address - for (int i=0; i < allLabels.size(); i++) { - if (allLabels[i].first == label && allLabels[i].second == address) { + for (int i = 0; i < allLabels.size(); i++) + { + if (allLabels[i].first == label && allLabels[i].second == address) + { allLabels.removeAt(i); writeToStorage(); return; @@ -338,10 +373,13 @@ void AddressBook::removeAddressLabel(QString label, QString address) { } } -void AddressBook::updateLabel(QString oldlabel, QString address, QString newlabel) { +void AddressBook::updateLabel(QString oldlabel, QString address, QString newlabel) +{ // Iterate over the list and update the label/address - for (int i = 0; i < allLabels.size(); i++) { - if (allLabels[i].first == oldlabel && allLabels[i].second == address) { + for (int i = 0; i < allLabels.size(); i++) + { + if (allLabels[i].first == oldlabel && allLabels[i].second == address) + { allLabels[i].first = newlabel; writeToStorage(); return; @@ -350,16 +388,20 @@ void AddressBook::updateLabel(QString oldlabel, QString address, QString newlabe } // Read all addresses -const QList>& AddressBook::getAllAddressLabels() { - if (allLabels.isEmpty()) { +const QList> &AddressBook::getAllAddressLabels() +{ + if (allLabels.isEmpty()) + { readFromStorage(); } return allLabels; } // Get the label for an address -QString AddressBook::getLabelForAddress(QString addr) { - for (auto i : allLabels) { +QString AddressBook::getLabelForAddress(QString addr) +{ + for (auto i : allLabels) + { if (i.second == addr) return i.first; } @@ -368,8 +410,10 @@ QString AddressBook::getLabelForAddress(QString addr) { } // Get the address for a label -QString AddressBook::getAddressForLabel(QString label) { - for (auto i: allLabels) { +QString AddressBook::getAddressForLabel(QString label) +{ + for (auto i : allLabels) + { if (i.first == label) return i.second; } @@ -377,7 +421,8 @@ QString AddressBook::getAddressForLabel(QString label) { return ""; } -QString AddressBook::addLabelToAddress(QString addr) { +QString AddressBook::addLabelToAddress(QString addr) +{ QString label = AddressBook::getInstance()->getLabelForAddress(addr); if (!label.isEmpty()) return label + "/" + addr; @@ -385,8 +430,9 @@ QString AddressBook::addLabelToAddress(QString addr) { return addr; } -QString AddressBook::addressFromAddressLabel(const QString& lblAddr) { - return lblAddr.trimmed().split("/").last(); +QString AddressBook::addressFromAddressLabel(const QString &lblAddr) +{ + return lblAddr.trimmed().split("/").last(); } -AddressBook* AddressBook::instance = nullptr; +AddressBook *AddressBook::instance = nullptr; diff --git a/src/connection.cpp b/src/connection.cpp index b710de7..f0f9250 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -1,6 +1,7 @@ // Copyright 2019-2023 The Hush developers // Released under the GPLv3 #include "connection.h" +#include "connection.h" #include "mainwindow.h" #include "settings.h" #include "ui_connection.h" @@ -9,23 +10,34 @@ #include "precompiled.h" #include "version.h" -extern bool isdragonx; - -ConnectionLoader::ConnectionLoader(MainWindow* main, RPC* rpc) { +extern bool isHSC; +extern std::string HSC_name; +extern std::string HSC_ac_name; +extern std::string HSC_port; +extern std::string App_title; +extern std::string HSC_website; +extern std::string HSC_param; +extern std::string HSC_app_name; +extern std::string HSC_website; + +ConnectionLoader::ConnectionLoader(MainWindow *main, RPC *rpc) +{ qDebug() << __func__; this->main = main; - this->rpc = rpc; + this->rpc = rpc; d = new QDialog(main); d->setWindowFlags(d->windowFlags() & ~(Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint)); connD = new Ui_ConnectionDialog(); connD->setupUi(d); - if(isdragonx) { - d->setWindowTitle("SilentDragonX"); + if (isHSC) + { + d->setWindowTitle(QString::fromStdString(App_title)); } - QMovie *movie1 = new QMovie(":/img/silentdragon-animated-startup-dark.gif");; + QMovie *movie1 = new QMovie(":/img/silentdragon-animated-startup-dark.gif"); + ; auto theme = Settings::getInstance()->get_theme_name(); - auto size = QSize(512,512); + auto size = QSize(512, 512); movie1->setScaledSize(size); connD->topIcon->setMovie(movie1); @@ -35,24 +47,29 @@ ConnectionLoader::ConnectionLoader(MainWindow* main, RPC* rpc) { qDebug() << "set animation"; } -ConnectionLoader::~ConnectionLoader() { +ConnectionLoader::~ConnectionLoader() +{ delete d; delete connD; main->logger->write("ConnectionLoader done"); qDebug() << "connection loader done"; } -void ConnectionLoader::loadConnection() { +void ConnectionLoader::loadConnection() +{ qDebug() << __func__; - QTimer::singleShot(1, [=]() { this->doAutoConnect(); }); + QTimer::singleShot(1, [=]() + { this->doAutoConnect(); }); if (!Settings::getInstance()->isHeadless()) d->exec(); } -void ConnectionLoader::doAutoConnect(bool tryEhushdStart) { +void ConnectionLoader::doAutoConnect(bool tryEhushdStart) +{ qDebug() << __func__; // Priority 1: Ensure all params are present. - if (!verifyParams()) { + if (!verifyParams()) + { qDebug() << "Cannot find sapling params!"; return; } @@ -61,15 +78,20 @@ void ConnectionLoader::doAutoConnect(bool tryEhushdStart) { auto config = autoDetectHushConf(); main->logger->write(QObject::tr("Attempting autoconnect")); - if (config.get() != nullptr) { + if (config.get() != nullptr) + { auto connection = makeConnection(config); - refreshHushdState(connection, [=] () { + refreshHushdState(connection, [=]() + { // Refused connection. So try and start embedded hushd if (Settings::getInstance()->useEmbedded()) { if (tryEhushdStart) { - if(isdragonx) { - this->showInformation(QObject::tr("Starting embedded dragonxd")); + if(isHSC) { + + std::string start_daemon = "Starting embedded" + HSC_name + "d"; + this->showInformation(QObject::tr(start_daemon.c_str())); + } else { this->showInformation(QObject::tr("Starting embedded hushd")); } @@ -101,21 +123,27 @@ void ConnectionLoader::doAutoConnect(bool tryEhushdStart) { "with SilentDragon\n\n." "Please remove the following line from your HUSH3.conf and restart SilentDragon\n" "daemon=1"); - if (isdragonx) { - explanation = QString() % QObject::tr("You have dragonxd set to start as a daemon, which can cause problems " - "with SilentDragonX\n\n." - "Please remove the following line from your DRAGONX.conf and restart SilentDragonX\n" - "daemon=1"); + if (isHSC) { + + std::string daemon_error = "You have " + HSC_name + "d set to start as a daemon, which can cause problems " + "with " + App_title + "\n\n." + "Please remove the following line from your " + HSC_ac_name + ".conf and restart " + App_title + "\n" + "daemon=1"; + + explanation = QString() % QObject::tr(daemon_error.c_str()); } } else { explanation = QString() % QObject::tr("Couldn't start the embedded hushd.\n\n" "Please try restarting.\n\nIf you previously started hushd with custom arguments, you might need to reset HUSH3.conf.\n\n" "If all else fails, please run hushd manually.") % (ehushd ? QObject::tr("The process returned") + ":\n\n" % ehushd->errorString() : QString("")); - if(isdragonx) { - explanation = QString() % QObject::tr("Couldn't start the embedded dragonxd.\n\n" - "Please try restarting.\n\nIf you previously started hushd with custom arguments, you might need to reset DRAGONX.conf.\n\n" - "If all else fails, please run dragonxd manually.") % + if(isHSC) { + + std::string daemon_error = "Couldn't start the embedded " + HSC_name + "d.\n\n" + "Please try restarting.\n\nIf you previously started hushd with custom arguments, you might need to reset " + HSC_ac_name + ".conf.\n\n" + "If all else fails, please run " + HSC_name + "d manually."; + + explanation = QString() % QObject::tr(daemon_error.c_str()) % (ehushd ? QObject::tr("The process returned") + ":\n\n" % ehushd->errorString() : QString("")); } } @@ -126,27 +154,35 @@ void ConnectionLoader::doAutoConnect(bool tryEhushdStart) { // HUSH3.conf exists, there's no connection, and the user asked us not to start hushd. Error! main->logger->write("Not using embedded and couldn't connect to hushd"); QString explanation = QString() % QObject::tr("Couldn't connect to hushd configured in HUSH3.conf.\n\n" - "Not starting embedded hushd because --no-embedded was passed"); - if(isdragonx) { - main->logger->write("Not using embedded and couldn't connect to dragonxd"); - QString explanation = QString() % QObject::tr("Couldn't connect to dragonxd configured in DRAGONX.conf.\n\n" - "Not starting embedded dragonxd because --no-embedded was passed"); + "Not starting embedded hushd because --no-embedded was passed"); + if(isHSC) { + std::string error = "Not using embedded and couldn't connect to " + HSC_name + "d"; + main->logger->write(error.c_str()); + + std::string conf_error = "Couldn't connect to " + HSC_name + "d configured in " + HSC_ac_name + ".conf.\n\n" + "Not starting embedded " + HSC_name + "d because --no-embedded was passed"; + QString explanation = QString() % QObject::tr(conf_error.c_str()); } this->showError(explanation); - } - }); - } else { - if (Settings::getInstance()->useEmbedded()) { + } }); + } + else + { + if (Settings::getInstance()->useEmbedded()) + { // HUSH3.conf was not found, so create one createHushConf(); - } else { + } + else + { // Fall back to manual connect doManualConnect(); } } } -QString randomPassword() { +QString randomPassword() +{ static const char alphanum[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -155,9 +191,10 @@ QString randomPassword() { // Assume we have low entropy randomness, // so we generate a longer password than we probably need const int passwordLength = 32; - char* s = new char[passwordLength + 1]; + char *s = new char[passwordLength + 1]; - for (int i = 0; i < passwordLength; ++i) { + for (int i = 0; i < passwordLength; ++i) + { #ifdef Q_OS_LINUX s[i] = alphanum[randombytes_uniform(sizeof(alphanum))]; #else @@ -172,7 +209,8 @@ QString randomPassword() { /** * This will create a new HUSH3.conf and download params if they cannot be found */ -void ConnectionLoader::createHushConf() { +void ConnectionLoader::createHushConf() +{ main->logger->write(__func__); auto confLocation = hushConfWritableLocation(); @@ -183,37 +221,38 @@ void ConnectionLoader::createHushConf() { ui.setupUi(&d); QPixmap logo(":/img/hushdlogo.png"); - ui.lblTopIcon->setBasePixmap(logo.scaled(512,512, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + ui.lblTopIcon->setBasePixmap(logo.scaled(512, 512, Qt::KeepAspectRatio, Qt::SmoothTransformation)); ui.btnPickDir->setEnabled(false); ui.grpAdvanced->setVisible(false); - QObject::connect(ui.btnAdvancedConfig, &QPushButton::toggled, [=](bool isVisible) { + QObject::connect(ui.btnAdvancedConfig, &QPushButton::toggled, [=](bool isVisible) + { ui.grpAdvanced->setVisible(isVisible); - ui.btnAdvancedConfig->setText(isVisible ? QObject::tr("Hide Advanced Config") : QObject::tr("Show Advanced Config")); - }); + ui.btnAdvancedConfig->setText(isVisible ? QObject::tr("Hide Advanced Config") : QObject::tr("Show Advanced Config")); }); - QObject::connect(ui.chkCustomDatadir, &QCheckBox::stateChanged, [=](int chked) { + QObject::connect(ui.chkCustomDatadir, &QCheckBox::stateChanged, [=](int chked) + { if (chked == Qt::Checked) { ui.btnPickDir->setEnabled(true); } else { ui.btnPickDir->setEnabled(false); - } - }); + } }); - QObject::connect(ui.btnPickDir, &QPushButton::clicked, [=]() { + QObject::connect(ui.btnPickDir, &QPushButton::clicked, [=]() + { auto datadir = QFileDialog::getExistingDirectory(main, QObject::tr("Choose data directory"), ui.lblDirName->text(), QFileDialog::ShowDirsOnly); if (!datadir.isEmpty()) { ui.lblDirName->setText(QDir::toNativeSeparators(datadir)); - } - }); + } }); // Show the dialog QString datadir = ""; bool useTor = false; - QString torPort = "9050"; + QString torPort = "9050"; - if (d.exec() == QDialog::Accepted) { + if (d.exec() == QDialog::Accepted) + { datadir = ui.lblDirName->text(); useTor = ui.chkUseTor->isChecked(); torPort = ui.torPort->text(); @@ -223,8 +262,10 @@ void ConnectionLoader::createHushConf() { QDir().mkpath(fi.dir().absolutePath()); QFile file(confLocation); - if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { - QString explanation = QString() % (isdragonx ? QObject::tr("Could not create DRAGONX.conf.") : QObject::tr("Could not create HUSH3.conf.") ); + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) + { + std::string error = "Could not create" + HSC_ac_name + ".conf."; + QString explanation = QString() % (isHSC ? QObject::tr(error.c_str()) : QObject::tr("Could not create HUSH3.conf.")); main->logger->write(explanation); this->showError(explanation); @@ -233,18 +274,28 @@ void ConnectionLoader::createHushConf() { QTextStream out(&file); - if(isdragonx) { - out << "# Autogenerated by Hush SilentDragonX " << APP_VERSION << " https://dragonx.is\n"; - } else { + if (isHSC) + { + std::string str1 = "# Autogenerated by Hush " + App_title + " "; + std::string str2 = " " + HSC_website; + out << str1.c_str() << APP_VERSION << str2.c_str(); + } + else + { out << "# Autogenerated by Hush SilentDragon " << APP_VERSION << " https://hush.is\n"; } out << "server=1\n"; out << "rpcpassword=" % randomPassword() << "\n"; - if(isdragonx) { - out << "rpcuser=dragonx\n"; - out << "rpcport=21769\n"; - } else { + if (isHSC) + { + std::string str1 = "rpcuser=" + HSC_name + "\n"; + std::string str2 = "rpcport=" + HSC_port + "\n"; + out << str1.c_str(); + out << str2.c_str(); + } + else + { out << "rpcuser=hush\n"; out << "rpcport=18031\n"; } @@ -262,10 +313,12 @@ void ConnectionLoader::createHushConf() { // this helps if older hushd's are being used out << "tls=only\n"; - if (!datadir.isEmpty()) { + if (!datadir.isEmpty()) + { out << "datadir=" % datadir % "\n"; } - if (useTor) { + if (useTor) + { out << "proxy=127.0.0.1:" << torPort << "\n"; } @@ -275,16 +328,18 @@ void ConnectionLoader::createHushConf() { this->doAutoConnect(); } - -void ConnectionLoader::doNextDownload(std::function cb) { - auto fnSaveFileName = [&] (QUrl url) { +void ConnectionLoader::doNextDownload(std::function cb) +{ + auto fnSaveFileName = [&](QUrl url) + { QString path = url.path(); QString basename = QFileInfo(path).fileName(); return basename; }; - if (downloadQueue->isEmpty()) { + if (downloadQueue->isEmpty()) + { delete downloadQueue; client->deleteLater(); @@ -300,7 +355,8 @@ void ConnectionLoader::doNextDownload(std::function cb) { QString filename = fnSaveFileName(url); QString paramsDir = zkParamsDir(); - if (QFile(QDir(paramsDir).filePath(filename)).exists()) { + if (QFile(QDir(paramsDir).filePath(filename)).exists()) + { main->logger->write(filename + " already exists, skipping"); doNextDownload(cb); @@ -310,7 +366,8 @@ void ConnectionLoader::doNextDownload(std::function cb) { // The downloaded file is written to a new name, and then renamed when the operation completes. currentOutput = new QFile(QDir(paramsDir).filePath(filename + ".part")); - if (!currentOutput->open(QIODevice::WriteOnly)) { + if (!currentOutput->open(QIODevice::WriteOnly)) + { main->logger->write("Couldn't open " + currentOutput->fileName() + " for writing"); this->showError(QObject::tr("Couldn't download params. Please check the help site for more info.")); } @@ -323,7 +380,8 @@ void ConnectionLoader::doNextDownload(std::function cb) { downloadTime.start(); // Download Progress - QObject::connect(currentDownload, &QNetworkReply::downloadProgress, [=] (auto done, auto total) { + QObject::connect(currentDownload, &QNetworkReply::downloadProgress, [=](auto done, auto total) + { // calculate the download speed double speed = done * 1000.0 / downloadTime.elapsed(); QString unit; @@ -339,11 +397,11 @@ void ConnectionLoader::doNextDownload(std::function cb) { this->showInformation( QObject::tr("Downloading ") % filename % (filesRemaining > 1 ? " ( +" % QString::number(filesRemaining) % QObject::tr(" more remaining )") : QString("")), - QString::number(done/1024/1024, 'f', 0) % QObject::tr("MB of ") % QString::number(total/1024/1024, 'f', 0) + QObject::tr("MB at ") % QString::number(speed, 'f', 2) % unit); - }); + QString::number(done/1024/1024, 'f', 0) % QObject::tr("MB of ") % QString::number(total/1024/1024, 'f', 0) + QObject::tr("MB at ") % QString::number(speed, 'f', 2) % unit); }); // Download Finished - QObject::connect(currentDownload, &QNetworkReply::finished, [=] () { + QObject::connect(currentDownload, &QNetworkReply::finished, [=]() + { // Rename file main->logger->write("Finished downloading " + filename); currentOutput->rename(QDir(paramsDir).filePath(filename)); @@ -357,16 +415,15 @@ void ConnectionLoader::doNextDownload(std::function cb) { this->showError(QObject::tr("Downloading ") + filename + QObject::tr(" failed. Please check the help site for more info")); } else { doNextDownload(cb); - } - }); + } }); // Download new data available. - QObject::connect(currentDownload, &QNetworkReply::readyRead, [=] () { - currentOutput->write(currentDownload->readAll()); - }); + QObject::connect(currentDownload, &QNetworkReply::readyRead, [=]() + { currentOutput->write(currentDownload->readAll()); }); } -bool ConnectionLoader::startEmbeddedHushd() { +bool ConnectionLoader::startEmbeddedHushd() +{ if (!Settings::getInstance()->useEmbedded()) return false; @@ -375,14 +432,19 @@ bool ConnectionLoader::startEmbeddedHushd() { // Static because it needs to survive even after this method returns. static QString processStdErrOutput; - if (ehushd != nullptr) { - if (ehushd->state() == QProcess::NotRunning) { - if (!processStdErrOutput.isEmpty()) { + if (ehushd != nullptr) + { + if (ehushd->state() == QProcess::NotRunning) + { + if (!processStdErrOutput.isEmpty()) + { QMessageBox::critical(main, QObject::tr("hushd error"), "hushd said: " + processStdErrOutput, QMessageBox::Ok); } return false; - } else { + } + else + { return true; } } @@ -394,43 +456,48 @@ bool ConnectionLoader::startEmbeddedHushd() { // params for DRGX are handled below #else auto hushdProgram = appPath.absoluteFilePath("hushd"); - if (isdragonx) { - hushdProgram = appPath.absoluteFilePath("dragonxd"); + if (isHSC) + { + std::string daemon = HSC_name + "d"; + hushdProgram = appPath.absoluteFilePath(daemon.c_str()); } #endif - //if (!QFile(hushdProgram).exists()) { - if (!QFile::exists(hushdProgram)) { + // if (!QFile(hushdProgram).exists()) { + if (!QFile::exists(hushdProgram)) + { qDebug() << "Can't find binary at " << hushdProgram; main->logger->write("Can't find binary at " + hushdProgram); return false; - } else { + } + else + { qDebug() << "Found binary at " << hushdProgram; main->logger->write("Found binary at " + hushdProgram); } ehushd = std::shared_ptr(new QProcess(main)); - QObject::connect(ehushd.get(), &QProcess::started, [=] () { - qDebug() << "Embedded binary started via " << hushdProgram; - }); + QObject::connect(ehushd.get(), &QProcess::started, [=]() + { qDebug() << "Embedded binary started via " << hushdProgram; }); QObject::connect(ehushd.get(), QOverload::of(&QProcess::finished), - [=](int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "hushd finished with code " << exitCode << "," << exitStatus; - }); + [=](int exitCode, QProcess::ExitStatus exitStatus) + { + qDebug() << "hushd finished with code " << exitCode << "," << exitStatus; + }); - QObject::connect(ehushd.get(), &QProcess::errorOccurred, [&] (QProcess::ProcessError error) { + QObject::connect(ehushd.get(), &QProcess::errorOccurred, [&](QProcess::ProcessError error) + { qDebug() << "Couldn't start hushd!"; - qDebug() << "hushd at " << hushdProgram << ":" << error; - }); + qDebug() << "hushd at " << hushdProgram << ":" << error; }); std::weak_ptr weak_obj(ehushd); auto ptr_main(main); - QObject::connect(ehushd.get(), &QProcess::readyReadStandardError, [weak_obj, ptr_main]() { + QObject::connect(ehushd.get(), &QProcess::readyReadStandardError, [weak_obj, ptr_main]() + { auto output = weak_obj.lock()->readAllStandardError(); ptr_main->logger->write("hushd stderr:" + output); - processStdErrOutput.append(output); - }); + processStdErrOutput.append(output); }); // This string should be the exact arg list seperated by single spaces // Could be modified to start different Hush Smart Chains @@ -445,9 +512,10 @@ bool ConnectionLoader::startEmbeddedHushd() { qDebug() << "No ASN map file found"; } */ - if(isdragonx) { + if (isHSC) + { // dragonxd bash script cannot be used on windows, so specify exact chain params - params += " -ac_name=DRAGONX -ac_algo=randomx -ac_halving=3500000 -ac_reward=300000000 -ac_blocktime=36 -ac_private=1 -addnode=176.126.87.241"; + params += QString::fromStdString(HSC_param); } QStringList arguments = params.split(" "); @@ -473,14 +541,15 @@ bool ConnectionLoader::startEmbeddedHushd() { return true; } -void ConnectionLoader::doManualConnect() { +void ConnectionLoader::doManualConnect() +{ auto config = loadFromSettings(); - if (!config) { + if (!config) + { // Nothing configured, show an error - QString explanation = QString() - % QObject::tr("A manual connection was requested, but the settings are not configured.\n\n" - "Please set the host/port and user/password in the Edit->Settings menu."); + QString explanation = QString() % QObject::tr("A manual connection was requested, but the settings are not configured.\n\n" + "Please set the host/port and user/password in the Edit->Settings menu."); showError(explanation); doRPCSetConnection(nullptr); @@ -489,7 +558,8 @@ void ConnectionLoader::doManualConnect() { } auto connection = makeConnection(config); - refreshHushdState(connection, [=] () { + refreshHushdState(connection, [=]() + { QString explanation = QString() % QObject::tr("Could not connect to hushd configured in settings.\n\n" "Please set the host/port and user/password in the Edit->Settings menu."); @@ -497,11 +567,11 @@ void ConnectionLoader::doManualConnect() { showError(explanation); doRPCSetConnection(nullptr); - return; - }); + return; }); } -void ConnectionLoader::doRPCSetConnection(Connection* conn) { +void ConnectionLoader::doRPCSetConnection(Connection *conn) +{ rpc->setEHushd(ehushd); rpc->setConnection(conn); @@ -510,15 +580,16 @@ void ConnectionLoader::doRPCSetConnection(Connection* conn) { delete this; } -Connection* ConnectionLoader::makeConnection(std::shared_ptr config) { - QNetworkAccessManager* client = new QNetworkAccessManager(main); +Connection *ConnectionLoader::makeConnection(std::shared_ptr config) +{ + QNetworkAccessManager *client = new QNetworkAccessManager(main); QUrl myurl; myurl.setScheme("http"); myurl.setHost(config.get()->host); myurl.setPort(config.get()->port.toInt()); - QNetworkRequest* request = new QNetworkRequest(); + QNetworkRequest *request = new QNetworkRequest(); request->setUrl(myurl); request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); @@ -529,45 +600,57 @@ Connection* ConnectionLoader::makeConnection(std::shared_ptr c return new Connection(main, client, request, config); } -void ConnectionLoader::refreshHushdState(Connection* connection, std::function refused) { +void ConnectionLoader::refreshHushdState(Connection *connection, std::function refused) +{ qDebug() << __func__ << ": refreshing state"; QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, - {"method", "getinfo"} - }; - connection->doRPC(payload, - [=] (auto) { + {"method", "getinfo"}}; + connection->doRPC( + payload, + [=](auto) + { // Success qDebug() << __func__ << ": hushd is online!"; // Delay 1 second to ensure loading (splash) is seen at least 1 second. - QTimer::singleShot(1000, [=]() { this->doRPCSetConnection(connection); }); + QTimer::singleShot(1000, [=]() + { this->doRPCSetConnection(connection); }); }, - [=] (QNetworkReply* reply, const QJsonValue &res) { + [=](QNetworkReply *reply, const QJsonValue &res) + { qDebug() << __func__ << ": failed to connect to hushd!"; // Failed, see what it is. auto err = reply->error(); - //qDebug() << err << res; + // qDebug() << err << res; - if (err == QNetworkReply::NetworkError::ConnectionRefusedError) { + if (err == QNetworkReply::NetworkError::ConnectionRefusedError) + { refused(); - } else if (err == QNetworkReply::NetworkError::AuthenticationRequiredError) { + } + else if (err == QNetworkReply::NetworkError::AuthenticationRequiredError) + { main->logger->write("Authentication failed"); QString explanation = QString() % - QObject::tr("Authentication failed. The username / password you specified was " - "not accepted by hushd. Try changing it in the Edit->Settings menu"); - if(isdragonx) { + QObject::tr("Authentication failed. The username / password you specified was " + "not accepted by hushd. Try changing it in the Edit->Settings menu"); + if (isHSC) + { + std::string = auth_failed = "Authentication failed. The username / password you specified was not " + "accepted by " + + HSC_name + "d. Try changing it in the Edit->Settings menu"; explanation = QString() % - QObject::tr("Authentication failed. The username / password you specified was " - "not accepted by dragonxd. Try changing it in the Edit->Settings menu"); + QObject::tr(auth_failed.c_str()); } this->showError(explanation); - } else if (err == QNetworkReply::NetworkError::InternalServerError && - !res.isNull()) { + } + else if (err == QNetworkReply::NetworkError::InternalServerError && + !res.isNull()) + { // The server is loading, so just poll until it succeeds - QString status = res["error"].toObject()["message"].toString(); + QString status = res["error"].toObject()["message"].toString(); { static int dots = 0; status = status.left(status.length() - 3) + QString(".").repeated(dots); @@ -575,28 +658,35 @@ void ConnectionLoader::refreshHushdState(Connection* connection, std::function 3) dots = 0; } - if(isdragonx) { - this->showInformation(QObject::tr("Your dragonxd is starting up. Please wait."), status); - } else { + if (isHSC) + { + std::string please_wait = "Your " + HSC_name + "d is starting up. Please wait."; + this->showInformation(QObject::tr(please_wait.c_str()), status); + } + else + { this->showInformation(QObject::tr("Your hushd is starting up. Please wait."), status); } main->logger->write("Waiting for hushd to come online."); // Refresh after one second - QTimer::singleShot(1000, [=]() { this->refreshHushdState(connection, refused); }); + QTimer::singleShot(1000, [=]() + { this->refreshHushdState(connection, refused); }); } - } - ); + }); } // Update the UI with the status -void ConnectionLoader::showInformation(QString info, QString detail) { +void ConnectionLoader::showInformation(QString info, QString detail) +{ static int rescanCount = 0; - if (detail.toLower().startsWith("rescan")) { - qDebug() << "showInformation detail = " +detail.toLower(); + if (detail.toLower().startsWith("rescan")) + { + qDebug() << "showInformation detail = " + detail.toLower(); rescanCount++; } - if (rescanCount > 10) { + if (rescanCount > 10) + { detail = detail + "\n" + QObject::tr("This may take several hours, grab some popcorn"); } @@ -609,8 +699,9 @@ void ConnectionLoader::showInformation(QString info, QString detail) { /** * Show error will close the loading dialog and show an error. -*/ -void ConnectionLoader::showError(QString explanation) { + */ +void ConnectionLoader::showError(QString explanation) +{ rpc->setEHushd(nullptr); rpc->noConnection(); @@ -618,11 +709,13 @@ void ConnectionLoader::showError(QString explanation) { d->close(); } -QString ConnectionLoader::locateHushConfFile() { +QString ConnectionLoader::locateHushConfFile() +{ // HSC's have no legacy locations - if (isdragonx) { - auto acname = "DRAGONX"; + if (isHSC) + { + auto acname = QString::fromStdString(HSC_ac_name); #ifdef Q_OS_LINUX auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, QString(".hush/") + acname + "/" + acname + ".conf"); #elif defined(Q_OS_DARWIN) @@ -636,19 +729,22 @@ QString ConnectionLoader::locateHushConfFile() { #ifdef Q_OS_LINUX auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".hush/HUSH3/HUSH3.conf"); - if(!QFile(confLocation).exists()) { + if (!QFile(confLocation).exists()) + { // legacy location confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".komodo/HUSH3/HUSH3.conf"); } #elif defined(Q_OS_DARWIN) auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "Library/Application Support/Hush/HUSH3/HUSH3.conf"); - if(!QFile(confLocation).exists()) { + if (!QFile(confLocation).exists()) + { // legacy location confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "Library/Application Support/Komodo/HUSH3/HUSH3.conf"); } #else auto confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Hush/HUSH3/HUSH3.conf"); - if(!QFile(confLocation).exists()) { + if (!QFile(confLocation).exists()) + { // legacy location confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Komodo/HUSH3/HUSH3.conf"); } @@ -659,21 +755,28 @@ QString ConnectionLoader::locateHushConfFile() { } // this function is only used for new config files and does not need to know about legacy locations -QString ConnectionLoader::hushConfWritableLocation() { +QString ConnectionLoader::hushConfWritableLocation() +{ #ifdef Q_OS_LINUX auto confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".hush/HUSH3/HUSH3.conf"); - if(isdragonx) { - confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".hush/DRAGONX/DRAGONX.conf"); + if (isHSC) + { + std::string conf_location = ".hush/" + HSC_ac_name + "/" + HSC_ac_name + ".conf"; + confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(conf_location.c_str()); } #elif defined(Q_OS_DARWIN) auto confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath("Library/Application Support/Hush/HUSH3/HUSH3.conf"); - if(isdragonx) { - confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath("Library/Application Support/Hush/DRAGONX/DRAGONX.conf"); + if (isHSC) + { + std::string conf_location = "Library/Application Support/Hush/" + HSC_ac_name + "/" + HSC_ac_name + ".conf"; + confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(conf_location.c_str()); } #else auto confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("../../Hush/HUSH3/HUSH3.conf"); - if(isdragonx) { - confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("../../Hush/DRAGONX/DRAGONX.conf"); + if (isHSC) + { + std::string conf_location = "../../Hush/" + HSC_ac_name + "/" + HSC_ac_name + ".conf"; + confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath(conf_location.c_str()); } #endif @@ -681,12 +784,14 @@ QString ConnectionLoader::hushConfWritableLocation() { return QDir::cleanPath(confLocation); } -QString ConnectionLoader::zkParamsDir() { +QString ConnectionLoader::zkParamsDir() +{ #ifdef Q_OS_LINUX - //TODO: If /usr/share/hush exists, use that. It should not be assumed writeable + // TODO: If /usr/share/hush exists, use that. It should not be assumed writeable auto paramsLocation = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".zcash-params")); // Debian packages do not install into per-user dirs - if (!paramsLocation.exists()) { + if (!paramsLocation.exists()) + { paramsLocation = QDir(QDir("/").filePath("usr/share/hush")); } #elif defined(Q_OS_DARWIN) @@ -695,7 +800,8 @@ QString ConnectionLoader::zkParamsDir() { auto paramsLocation = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("../../ZcashParams")); #endif - if (!paramsLocation.exists()) { + if (!paramsLocation.exists()) + { main->logger->write("Creating params location at " + paramsLocation.absolutePath()); QDir().mkpath(paramsLocation.absolutePath()); } @@ -704,54 +810,63 @@ QString ConnectionLoader::zkParamsDir() { return paramsLocation.absolutePath(); } -bool ConnectionLoader::verifyParams() { +bool ConnectionLoader::verifyParams() +{ QDir paramsDir(zkParamsDir()); // TODO: better error reporting if only 1 file exists or is missing qDebug() << "Verifying sapling param files exist"; - // This list of locations to look must be kept in sync with the list in hushd - if( QFile( QDir(".").filePath("sapling-output.params") ).exists() && QFile( QDir(".").filePath("sapling-spend.params") ).exists() ) { + if (QFile(QDir(".").filePath("sapling-output.params")).exists() && QFile(QDir(".").filePath("sapling-spend.params")).exists()) + { qDebug() << "Found params in ."; return true; } - if( QFile( QDir("..").filePath("sapling-output.params") ).exists() && QFile( QDir("..").filePath("sapling-spend.params") ).exists() ) { + if (QFile(QDir("..").filePath("sapling-output.params")).exists() && QFile(QDir("..").filePath("sapling-spend.params")).exists()) + { qDebug() << "Found params in .."; return true; } - if( QFile( QDir("..").filePath("hush3/sapling-output.params") ).exists() && QFile( QDir("..").filePath("hush3/sapling-spend.params") ).exists() ) { + if (QFile(QDir("..").filePath("hush3/sapling-output.params")).exists() && QFile(QDir("..").filePath("hush3/sapling-spend.params")).exists()) + { qDebug() << "Found params in ../hush3"; return true; } // this is to support SD on mac in /Applications - if( QFile( QDir("/Applications").filePath("silentdragon.app/Contents/MacOS/sapling-output.params") ).exists() && QFile( QDir("/Applications").filePath("./silentdragon.app/Contents/MacOS/sapling-spend.params") ).exists() ) { + if (QFile(QDir("/Applications").filePath("silentdragon.app/Contents/MacOS/sapling-output.params")).exists() && QFile(QDir("/Applications").filePath("./silentdragon.app/Contents/MacOS/sapling-spend.params")).exists()) + { qDebug() << "Found params in /Applications/silentdragon.app/Contents/MacOS"; return true; } // this is to support SD on mac inside a DMG - if( QFile( QDir("./").filePath("silentdragon.app/Contents/MacOS/sapling-output.params") ).exists() && QFile( QDir("./").filePath("./silentdragon.app/Contents/MacOS/sapling-spend.params") ).exists() ) { + if (QFile(QDir("./").filePath("silentdragon.app/Contents/MacOS/sapling-output.params")).exists() && QFile(QDir("./").filePath("./silentdragon.app/Contents/MacOS/sapling-spend.params")).exists()) + { qDebug() << "Found params in ./silentdragon.app/Contents/MacOS"; return true; } // this is to support SDX on mac in /Applications - if( QFile( QDir("/Applications").filePath("silentdragonx.app/Contents/MacOS/sapling-output.params") ).exists() && QFile( QDir("/Applications").filePath("./silentdragonx.app/Contents/MacOS/sapling-spend.params") ).exists() ) { - qDebug() << "Found params in /Applications/silentdragonx.app/Contents/MacOS"; + QString app_name = QString::fromStdString(HSC_app_name); + if (QFile(QDir("/Applications").filePath("" + app_name + ".app/Contents/MacOS/sapling-output.params")).exists() && QFile(QDir("/Applications").filePath("./" + app_name + ".app/Contents/MacOS/sapling-spend.params")).exists()) + { + qDebug() << "Found params in /Applications/" + app_name + ".app/Contents/MacOS"; return true; } // this is to support SDX on mac inside a DMG - if( QFile( QDir("./").filePath("silentdragonx.app/Contents/MacOS/sapling-output.params") ).exists() && QFile( QDir("./").filePath("./silentdragonx.app/Contents/MacOS/sapling-spend.params") ).exists() ) { - qDebug() << "Found params in ./silentdragonx.app/Contents/MacOS"; + if (QFile(QDir("./").filePath("" + app_name + ".app/Contents/MacOS/sapling-output.params")).exists() && QFile(QDir("./").filePath("./" + app_name + ".app/Contents/MacOS/sapling-spend.params")).exists()) + { + qDebug() << "Found params in ./" + app_name + ".app/Contents/MacOS"; return true; } - if (QFile(paramsDir.filePath("sapling-output.params")).exists() && QFile(paramsDir.filePath("sapling-spend.params")).exists()) { + if (QFile(paramsDir.filePath("sapling-output.params")).exists() && QFile(paramsDir.filePath("sapling-spend.params")).exists()) + { qDebug() << "Found params in " << paramsDir; return true; } @@ -764,16 +879,19 @@ bool ConnectionLoader::verifyParams() { * Try to automatically detect a HUSH3/HUSH3.conf file or DRAGONX/DRAGONX.conf in the correct location and load parameters */ -std::shared_ptr ConnectionLoader::autoDetectHushConf() { +std::shared_ptr ConnectionLoader::autoDetectHushConf() +{ auto confLocation = locateHushConfFile(); - if (confLocation.isNull()) { + if (confLocation.isNull()) + { // No file, just return with nothing return nullptr; } QFile file(confLocation); - if (!file.open(QIODevice::ReadOnly)) { + if (!file.open(QIODevice::ReadOnly)) + { qDebug() << file.errorString(); return nullptr; } @@ -781,7 +899,7 @@ std::shared_ptr ConnectionLoader::autoDetectHushConf() { QTextStream in(&file); auto hushconf = new ConnectionConfig(); - hushconf->host = "127.0.0.1"; + hushconf->host = "127.0.0.1"; hushconf->connType = ConnectionType::DetectedConfExternalHushD; hushconf->usingHushConf = true; hushconf->hushDir = QFileInfo(confLocation).absoluteDir().absolutePath(); @@ -789,51 +907,65 @@ std::shared_ptr ConnectionLoader::autoDetectHushConf() { Settings::getInstance()->setUsingHushConf(confLocation); - while (!in.atEnd()) { + while (!in.atEnd()) + { QString line = in.readLine(); auto s = line.indexOf("="); - QString name = line.left(s).trimmed().toLower(); + QString name = line.left(s).trimmed().toLower(); QString value = line.right(line.length() - s - 1).trimmed(); - if (name == "rpcuser") { + if (name == "rpcuser") + { hushconf->rpcuser = value; } - if (name == "rpcpassword") { + if (name == "rpcpassword") + { hushconf->rpcpassword = value; } - if (name == "rpcport") { + if (name == "rpcport") + { hushconf->port = value; } - if (name == "daemon" && value == "1") { + if (name == "daemon" && value == "1") + { hushconf->hushDaemon = true; } - if (name == "proxy") { + if (name == "proxy") + { hushconf->proxy = value; } - if (name == "consolidation") { + if (name == "consolidation") + { hushconf->consolidation = value; } - if (name == "deletetx") { + if (name == "deletetx") + { hushconf->deletetx = value; } - if (name == "zindex") { + if (name == "zindex") + { hushconf->zindex = value; } if (name == "testnet" && - value == "1" && - hushconf->port.isEmpty()) { - hushconf->port = "18232"; + value == "1" && + hushconf->port.isEmpty()) + { + hushconf->port = "18232"; } } // If rpcport is not in the file, and it was not set by the testnet=1 flag, then go to default - if (hushconf->port.isEmpty()) { - if(isdragonx) { - hushconf->port = "21769"; - } else { + if (hushconf->port.isEmpty()) + { + if (isHSC) + { + hushconf->port = HSC_port; + } + else + { hushconf->port = "18031"; } - } + } file.close(); // Save to Qsettings @@ -849,21 +981,23 @@ std::shared_ptr ConnectionLoader::autoDetectHushConf() { } // Load connection settings from the UI, which indicates an unknown, external hushd -std::shared_ptr ConnectionLoader::loadFromSettings() { - qDebug() << __func__ <<": Load data from the QT Settings"; +std::shared_ptr ConnectionLoader::loadFromSettings() +{ + qDebug() << __func__ << ": Load data from the QT Settings"; QSettings s; - auto host = s.value("connection/host").toString(); - auto port = s.value("connection/port").toString(); - auto username = s.value("connection/rpcuser").toString(); - auto password = s.value("connection/rpcpassword").toString(); + auto host = s.value("connection/host").toString(); + auto port = s.value("connection/port").toString(); + auto username = s.value("connection/rpcuser").toString(); + auto password = s.value("connection/rpcpassword").toString(); - if (username.isEmpty() || password.isEmpty()) { - qDebug() << __func__ <<": username or password empty, returning null!"; + if (username.isEmpty() || password.isEmpty()) + { + qDebug() << __func__ << ": username or password empty, returning null!"; return nullptr; } - auto uiConfig = new ConnectionConfig{ host, port, username, password, false, false,"","", "", "","", ConnectionType::UISettingsZCashD}; + auto uiConfig = new ConnectionConfig{host, port, username, password, false, false, "", "", "", "", "", ConnectionType::UISettingsZCashD}; return std::shared_ptr(uiConfig); } @@ -871,30 +1005,37 @@ std::shared_ptr ConnectionLoader::loadFromSettings() { /*********************************************************************************** * Connection Class ************************************************************************************/ -Connection::Connection(MainWindow* m, QNetworkAccessManager* c, QNetworkRequest* r, - std::shared_ptr conf) { - this->restclient = c; - this->request = r; - this->config = conf; - this->main = m; +Connection::Connection(MainWindow *m, QNetworkAccessManager *c, QNetworkRequest *r, + std::shared_ptr conf) +{ + this->restclient = c; + this->request = r; + this->config = conf; + this->main = m; } -Connection::~Connection() { +Connection::~Connection() +{ delete restclient; delete request; } -void Connection::doRPC(const QJsonValue& payload, const std::function& cb, - const std::function& ne) { - if (shutdownInProgress) { +void Connection::doRPC(const QJsonValue &payload, const std::function &cb, + const std::function &ne) +{ + if (shutdownInProgress) + { qDebug() << __func__ << ": Ignoring RPC because shutdown in progress"; return; } - if(payload.isNull() || payload.isUndefined()) { + if (payload.isNull() || payload.isUndefined()) + { qDebug() << "no payload! ignoring"; return; - } else { + } + else + { qDebug() << __func__ << ": " << payload["method"].toString() << payload; } @@ -903,7 +1044,8 @@ void Connection::doRPC(const QJsonValue& payload, const std::functionpost(*request, ba_rpc_call); - QObject::connect(reply, &QNetworkReply::finished, [=] { + QObject::connect(reply, &QNetworkReply::finished, [=] + { reply->deleteLater(); if (shutdownInProgress) { // Ignoring callback because shutdown in progress @@ -927,28 +1069,32 @@ void Connection::doRPC(const QJsonValue& payload, const std::function& cb) { - doRPC(payload, cb, [=] (QNetworkReply* reply, const QJsonValue &parsed) { +void Connection::doRPCWithDefaultErrorHandling(const QJsonValue &payload, const std::function &cb) +{ + doRPC(payload, cb, [=](QNetworkReply *reply, const QJsonValue &parsed) + { if (!parsed.isUndefined() && !parsed["error"].toObject()["message"].isNull()) { this->showTxError(parsed["error"].toObject()["message"].toString()); } else { this->showTxError(reply->errorString()); - } - }); + } }); } -void Connection::doRPCIgnoreError(const QJsonValue& payload, const std::function& cb) { - doRPC(payload, cb, [=] (auto, auto) { - // Ignored error handling - }); +void Connection::doRPCIgnoreError(const QJsonValue &payload, const std::function &cb) +{ + doRPC(payload, cb, [=](auto, auto) + { + // Ignored error handling + }); } -void Connection::showTxError(const QString& error) { - if (error.isNull()) return; +void Connection::showTxError(const QString &error) +{ + if (error.isNull()) + return; // Prevent multiple dialog boxes from showing, because they're all called async static bool shown = false; @@ -956,14 +1102,14 @@ void Connection::showTxError(const QString& error) { return; shown = true; - QMessageBox::critical(main, QObject::tr("Transaction Error"), QObject::tr("There was an error! : ") + "\n\n" - + error, QMessageBox::StandardButton::Ok); + QMessageBox::critical(main, QObject::tr("Transaction Error"), QObject::tr("There was an error! : ") + "\n\n" + error, QMessageBox::StandardButton::Ok); shown = false; } /** * Prevent all future calls from going through */ -void Connection::shutdown() { +void Connection::shutdown() +{ shutdownInProgress = true; } diff --git a/src/main.cpp b/src/main.cpp index 45e3323..2f53b4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,9 +7,21 @@ #include "settings.h" #include "version.h" -bool isdragonx = 0; - -class SignalHandler +bool isHSC = 1; +std::string HSC_name = "dragonx"; +std::string HSC_ticker = "drgx"; +std::string HSC_default_theme = "dragonx"; +std::string HSC_app_name = "silentdragonx"; +std::string HSC_TG_link = "dragonx.is/tg"; +std::string HSC_website = "dragonx.is"; +std::string HSC_explorer = "explorer.dragonx.is"; +std::string App_title = "SilentDragonX"; + +std::string HSC_ac_name = "DRAGONX"; +std::string HSC_port = "21769"; +std::string HSC_param = " -ac_name=" + HSC_ac_name + " -ac_algo=randomx -ac_halving=3500000 -ac_reward=300000000 -ac_blocktime=36 -ac_private=1 -addnode=176.126.87.241"; + +class SignalHandler { public: SignalHandler(int mask = DEFAULT_SIGNALS); @@ -17,12 +29,12 @@ public: enum SIGNALS { - SIG_UNHANDLED = 0, // Physical signal not supported by this class - SIG_NOOP = 1, // The application is requested to do a no-op (only a target that platform-specific signals map to when they can't be raised anyway) - SIG_INT = 2, // Control+C (should terminate but consider that it's a normal way to do so; can delay a bit) - SIG_TERM = 4, // Control+Break (should terminate now without regarding the consquences) - SIG_CLOSE = 8, // Container window closed (should perform normal termination, like Ctrl^C) [Windows only; on Linux it maps to SIG_TERM] - SIG_RELOAD = 16, // Reload the configuration [Linux only, physical signal is SIGHUP; on Windows it maps to SIG_NOOP] + SIG_UNHANDLED = 0, // Physical signal not supported by this class + SIG_NOOP = 1, // The application is requested to do a no-op (only a target that platform-specific signals map to when they can't be raised anyway) + SIG_INT = 2, // Control+C (should terminate but consider that it's a normal way to do so; can delay a bit) + SIG_TERM = 4, // Control+Break (should terminate now without regarding the consquences) + SIG_CLOSE = 8, // Container window closed (should perform normal termination, like Ctrl^C) [Windows only; on Linux it maps to SIG_TERM] + SIG_RELOAD = 16, // Reload the configuration [Linux only, physical signal is SIGHUP; on Windows it maps to SIG_NOOP] DEFAULT_SIGNALS = SIG_INT | SIG_TERM | SIG_CLOSE, }; static const int numSignals = 6; @@ -44,7 +56,7 @@ private: #endif //!_WIN32 // There can be only ONE SignalHandler per process -SignalHandler* g_handler(NULL); +SignalHandler *g_handler(NULL); #ifndef _WIN32 @@ -59,7 +71,7 @@ SignalHandler::SignalHandler(int mask) : _mask(mask) assert(g_handler == NULL); g_handler = this; - for (int i=0;isetFont(QFont("Ubuntu", 11, QFont::Normal, false)); - #endif +// TODO: update for SD +#ifdef Q_OS_LINUX + QFontDatabase::addApplicationFont(":/fonts/Ubuntu-R.ttf"); + qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false)); +#endif // QRandomGenerator generates a secure random number, which we use to seed. - #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) unsigned int seed = QRandomGenerator::securelySeeded().generate(); - #else - // This will be used only during debugging for compatibility reasons +#else + // This will be used only during debugging for compatibility reasons unsigned int seed = std::time(0); - #endif +#endif std::srand(seed); Settings::init(); // Set up libsodium - if (sodium_init() < 0) { + if (sodium_init() < 0) + { /* panic! the library couldn't be initialized, it is not safe to use */ qDebug() << "libsodium is not initialized!"; exit(0); } // Check for embedded option - if (parser.isSet(noembeddedOption)) { + if (parser.isSet(noembeddedOption)) + { Settings::getInstance()->setUseEmbedded(false); - } else { + } + else + { Settings::getInstance()->setUseEmbedded(true); } w = new MainWindow(); - if(isdragonx) { - w->setWindowTitle("SilentDragonX v" + QString(APP_VERSION)); - } else { + if (isHSC) + { + w->setWindowTitle((QString::fromStdString(App_title) + " v" + QString(APP_VERSION))); + } + else + { w->setWindowTitle("SilentDragon v" + QString(APP_VERSION)); } // If there was a payment URI on the command line, pay it - if (parser.positionalArguments().length() > 0) { + if (parser.positionalArguments().length() > 0) + { w->payHushURI(parser.positionalArguments()[0]); } // Listen for any secondary instances telling us about a Hush payment URI - QObject::connect(&a, &SingleApplication::receivedMessage, [=] (quint32, QByteArray msg) { + QObject::connect(&a, &SingleApplication::receivedMessage, [=](quint32, QByteArray msg) + { QString uri(msg); // We need to execute this async, otherwise the app seems to crash for some reason. - QTimer::singleShot(1, [=]() { w->payHushURI(uri); }); - }); + QTimer::singleShot(1, [=]() { w->payHushURI(uri); }); }); // For MacOS, we have an event filter a.installEventFilter(w); // Check if starting headless - if (parser.isSet(headlessOption)) { + if (parser.isSet(headlessOption)) + { Settings::getInstance()->setHeadless(true); - a.setQuitOnLastWindowClosed(false); - } else { + a.setQuitOnLastWindowClosed(false); + } + else + { Settings::getInstance()->setHeadless(false); w->show(); } @@ -271,42 +307,44 @@ public: void DispatchToMainThread(std::function callback) { // any thread - QTimer* timer = new QTimer(); + QTimer *timer = new QTimer(); timer->moveToThread(qApp->thread()); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, [=]() - { + { // main thread callback(); - timer->deleteLater(); - }); + timer->deleteLater(); }); QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0)); } bool handleSignal(int signal) { - std::cout << std::endl << "Interrupted with signal " << signal << std::endl; - - if (w && w->getRPC()) { + std::cout << std::endl + << "Interrupted with signal " << signal << std::endl; + + if (w && w->getRPC()) + { // Blocking call to closeEvent on the UI thread. - DispatchToMainThread([=] { + DispatchToMainThread([=] + { w->doClose(); - QApplication::quit(); - }); - } else { + QApplication::quit(); }); + } + else + { QApplication::quit(); } - + return true; } private: - MainWindow* w; + MainWindow *w; }; -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { Application app; return app.main(argc, argv); } - diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 04f6c99..366da4c 100755 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -24,24 +24,33 @@ #include #include "sd.h" -extern bool isdragonx; - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) +extern bool isHSC; +extern std::string HSC_default_theme; +extern std::string HSC_ticker; +extern std::string App_title; +extern std::string HSC_website; +extern std::string HSC_ac_name; +extern std::string HSC_name; + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), + ui(new Ui::MainWindow) { // Include css QString theme_name; try { - theme_name = Settings::getInstance()->get_theme_name(); - } catch (...) + theme_name = Settings::getInstance()->get_theme_name(); + } + catch (...) { qDebug() << __func__ << ": exception!"; - if(isdragonx){ - theme_name = "dragonx"; - }else{ + if (isHSC) + { + theme_name = QString::fromStdString(HSC_default_theme); + } + else + { theme_name = "dark"; } } @@ -49,7 +58,7 @@ MainWindow::MainWindow(QWidget *parent) : this->slot_change_theme(theme_name); ui->setupUi(this); - logger = new Logger(this, QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath(isdragonx ? "SilentDragonX.log" : "SilentDragon.log")); + logger = new Logger(this, QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath(isHSC ? (QString::fromStdString(App_title) + ".log") : "SilentDragon.log")); // Status Bar setupStatusBar(); @@ -64,20 +73,18 @@ MainWindow::MainWindow(QWidget *parent) : QObject::connect(ui->actionWebsite, &QAction::triggered, this, &MainWindow::website); // Set up check for updates action - QObject::connect(ui->actionCheck_for_Updates, &QAction::triggered, [=] () { + QObject::connect(ui->actionCheck_for_Updates, &QAction::triggered, [=]() + { // Silent is false, so show notification even if no update was found - rpc->checkForUpdate(false); - }); + rpc->checkForUpdate(false); }); // Request hush - QObject::connect(ui->actionRequest_hush, &QAction::triggered, [=]() { - RequestDialog::showRequestZcash(this); - }); + QObject::connect(ui->actionRequest_hush, &QAction::triggered, [=]() + { RequestDialog::showRequestZcash(this); }); // Pay Hush URI - QObject::connect(ui->actionPay_URI, &QAction::triggered, [=] () { - payHushURI(); - }); + QObject::connect(ui->actionPay_URI, &QAction::triggered, [=]() + { payHushURI(); }); // Import Private Key QObject::connect(ui->actionImport_Private_Key, &QAction::triggered, this, &MainWindow::importPrivKey); @@ -98,7 +105,8 @@ MainWindow::MainWindow(QWidget *parent) : QObject::connect(ui->action_Address_Book, &QAction::triggered, this, &MainWindow::addressBook); // Set up about action - QObject::connect(ui->actionAbout, &QAction::triggered, [=] () { + QObject::connect(ui->actionAbout, &QAction::triggered, [=]() + { QDialog aboutDialog(this); Ui_about about; about.setupUi(&aboutDialog); @@ -107,19 +115,17 @@ MainWindow::MainWindow(QWidget *parent) : QString version = QString("Version ") % QString(APP_VERSION) % " (" % QString(__DATE__) % ") using QT " % qVersion(); about.versionLabel->setText(version); - aboutDialog.exec(); - }); + aboutDialog.exec(); }); // Initialize to the balances tab ui->tabWidget->setCurrentIndex(0); - setupSendTab(); setupTransactionsTab(); setupReceiveTab(); setupBalancesTab(); setupMarketTab(); - //setupChatTab(); + // setupChatTab(); setupHushTab(); setupPeersTab(); @@ -131,7 +137,8 @@ MainWindow::MainWindow(QWidget *parent) : restoreSavedStates(); } -void MainWindow::restoreSavedStates() { +void MainWindow::restoreSavedStates() +{ QSettings s; restoreGeometry(s.value("geometry").toByteArray()); @@ -139,73 +146,88 @@ void MainWindow::restoreSavedStates() { ui->transactionsTable->horizontalHeader()->restoreState(s.value("tratablegeometry").toByteArray()); } -void MainWindow::doClose() { +void MainWindow::doClose() +{ closeEvent(nullptr); } -void MainWindow::retranslateMiningTab() { +void MainWindow::retranslateMiningTab() +{ DEBUG("retranslating mining tab"); auto tab = ui->tabWidget->template findChild("Mining"); - if(tab != nullptr) { + if (tab != nullptr) + { DEBUG("found Mining tab"); ui->tabWidget->setTabText(ui->tabWidget->indexOf(tab), QObject::tr("Mining")); - } else { + } + else + { DEBUG("no Mining tab found!"); return; } auto button1 = tab->template findChild("stopmining"); - if(button1 != nullptr) { + if (button1 != nullptr) + { DEBUG("found stop mining button " << button1->objectName()); - button1->setText( QObject::tr("Stop Mining") ); + button1->setText(QObject::tr("Stop Mining")); } auto button2 = tab->template findChild("startmining"); - if(button2 != nullptr) { + if (button2 != nullptr) + { DEBUG("found start mining button " << button2->objectName()); - button2->setText( QObject::tr("Start Mining") ); + button2->setText(QObject::tr("Start Mining")); button2->setObjectName("startmining"); } auto label2 = tab->findChild("mininglabel2"); - if(label2 != nullptr) { + if (label2 != nullptr) + { DEBUG("found mininglabel2"); - label2->setText( QObject::tr("Mining threads") ); + label2->setText(QObject::tr("Mining threads")); } auto label3 = tab->findChild("mininglabel3"); - if(label3 != nullptr) { - label3->setText( QObject::tr("Local Hashrate (hashes/sec)") ); + if (label3 != nullptr) + { + label3->setText(QObject::tr("Local Hashrate (hashes/sec)")); } auto label4 = tab->findChild("mininglabel4"); - if(label4 != nullptr) { - label4->setText( QObject::tr("Network Hashrate (hashes/sec)") ); + if (label4 != nullptr) + { + label4->setText(QObject::tr("Network Hashrate (hashes/sec)")); } auto label5 = tab->findChild("mininglabel5"); - if(label5 != nullptr) { - label5->setText( QObject::tr("Difficulty") ); + if (label5 != nullptr) + { + label5->setText(QObject::tr("Difficulty")); } auto label6 = tab->findChild("mininglabel6"); - if(label6 != nullptr) { - label6->setText( QObject::tr("Estimated Hours To Find A Block") ); + if (label6 != nullptr) + { + label6->setText(QObject::tr("Estimated Hours To Find A Block")); } auto label7 = tab->findChild("mininglabel7"); - if(label7 != nullptr) { - label7->setText( QObject::tr("Select the number of threads to mine with:") ); + if (label7 != nullptr) + { + label7->setText(QObject::tr("Select the number of threads to mine with:")); } auto combo = tab->findChild("genproclimit"); - if(combo != nullptr) { + if (combo != nullptr) + { DEBUG("found genproclimit combo"); int hwc = std::thread::hardware_concurrency(); // TODO: Is there a better way than recreating this combobox? combo->clear(); - auto threadStr = tr("thread"); + auto threadStr = tr("thread"); auto threadsStr = tr("threads"); // 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) % " " % (i+1==1 ? threadStr : threadsStr)); + for (int i = 0; i < hwc / 2; i++) + { + combo->insertItem(i, QString::number(i + 1) % " " % (i + 1 == 1 ? threadStr : threadsStr)); } } } @@ -214,7 +236,8 @@ void MainWindow::retranslateMiningTab() { void MainWindow::slotLanguageChanged(QString lang) { qDebug() << __func__ << ": lang=" << lang; - if(lang != "") { + if (lang != "") + { // load the language loadLanguage(lang); @@ -224,38 +247,44 @@ void MainWindow::slotLanguageChanged(QString lang) retranslateMiningTab(); } - } - -void switchTranslator(QTranslator& translator, const QString& filename) { +void switchTranslator(QTranslator &translator, const QString &filename) +{ qDebug() << __func__ << ": filename=" << filename; // remove the old translator qApp->removeTranslator(&translator); // load the new translator QString path = QApplication::applicationDirPath(); - if (isdragonx) { - path.append("/res-drgx/"); + if (isHSC) + { + path.append(("/res-" + QString::fromStdString(HSC_ticker) + "/")); } - else{ + else + { path.append("/res/"); } qDebug() << __func__ << ": attempting to load " << path + filename; - if(translator.load(path + filename)) { + if (translator.load(path + filename)) + { qApp->installTranslator(&translator); - } else { + } + else + { qDebug() << __func__ << ": translation path does not exist! " << path + filename; } } -void MainWindow::loadLanguage(QString& rLanguage) { +void MainWindow::loadLanguage(QString &rLanguage) +{ qDebug() << __func__ << ": currLang=" << m_currLang << " rLanguage=" << rLanguage; QString lang = rLanguage; // this allows us to call this function with just a locale such as "zh" - if(lang.right(1) == ")") { + if (lang.right(1) == ")") + { lang.chop(1); // remove trailing ) } @@ -266,7 +295,8 @@ void MainWindow::loadLanguage(QString& rLanguage) { // https://www.loc.gov/standards/iso639-2/php/code_list.php QString languageName; - if(m_currLang != lang) { + if (m_currLang != lang) + { qDebug() << __func__ << ": changing language to lang=" << lang; m_currLang = lang; QLocale locale = QLocale(m_currLang); @@ -274,7 +304,8 @@ void MainWindow::loadLanguage(QString& rLanguage) { qDebug() << __func__ << ": locale nativeLanguage=" << locale.nativeLanguageName(); // an invalid locale such as "zz" will give the C locale which has no native language name - if (locale.nativeLanguageName() == "") { + if (locale.nativeLanguageName() == "") + { qDebug() << __func__ << ": detected invalid language in config file, defaulting to en"; locale = QLocale("en"); Settings::getInstance()->set_language("en"); @@ -284,7 +315,7 @@ void MainWindow::loadLanguage(QString& rLanguage) { qDebug() << __func__ << ": locale=" << locale; QLocale::setDefault(locale); qDebug() << __func__ << ": setDefault locale=" << locale; - languageName = locale.nativeLanguageName(); //locale.language()); + languageName = locale.nativeLanguageName(); // locale.language()); qDebug() << __func__ << ": languageName=" << languageName; switchTranslator(m_translator, QString("silentdragon_%1.qm").arg(lang)); @@ -292,47 +323,52 @@ void MainWindow::loadLanguage(QString& rLanguage) { // TODO: this likely wont work for RTL languages like Arabic auto first = QString(languageName.at(0)).toUpper(); - languageName = first + languageName.right(languageName.size()-1); - if( lang == "en" ) { - languageName.replace("American ",""); + languageName = first + languageName.right(languageName.size() - 1); + if (lang == "en") + { + languageName.replace("American ", ""); } ui->statusBar->showMessage(tr("Language changed to") + " " + languageName + " (" + lang + ")"); } // write this language (the locale shortcode) out to config file - if (lang != "") { + if (lang != "") + { // only write valid languages to config file Settings::getInstance()->set_language(lang); } } -void MainWindow::changeEvent(QEvent* event) { - if(0 != event) { - switch(event->type()) { - // this event is sent if a translator is loaded - case QEvent::LanguageChange: - qDebug() << __func__ << ": QEvent::LanguageChange changeEvent"; - ui->retranslateUi(this); - break; - - // this event is sent, if the system, language changes - case QEvent::LocaleChange: - { - QString locale = QLocale::system().name(); - locale.truncate(locale.lastIndexOf('_')); - qDebug() << __func__ << ": QEvent::LocaleChange changeEvent locale=" << locale; - loadLanguage(locale); - } - break; - default: - qDebug() << __func__ << ": " << event->type(); - } - } - QMainWindow::changeEvent(event); +void MainWindow::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + // this event is sent if a translator is loaded + case QEvent::LanguageChange: + qDebug() << __func__ << ": QEvent::LanguageChange changeEvent"; + ui->retranslateUi(this); + break; + + // this event is sent, if the system, language changes + case QEvent::LocaleChange: + { + QString locale = QLocale::system().name(); + locale.truncate(locale.lastIndexOf('_')); + qDebug() << __func__ << ": QEvent::LocaleChange changeEvent locale=" << locale; + loadLanguage(locale); + } + break; + default: + qDebug() << __func__ << ": " << event->type(); + } + } + QMainWindow::changeEvent(event); } - -void MainWindow::closeEvent(QCloseEvent* event) { +void MainWindow::closeEvent(QCloseEvent *event) +{ QSettings s; s.setValue("geometry", saveGeometry()); @@ -349,7 +385,8 @@ void MainWindow::closeEvent(QCloseEvent* event) { QMainWindow::closeEvent(event); } -void MainWindow::setupStatusBar() { +void MainWindow::setupStatusBar() +{ // Status Bar loadingLabel = new QLabel(); loadingMovie = new QMovie(":/icons/loading.gif"); @@ -363,7 +400,8 @@ void MainWindow::setupStatusBar() { // Custom status bar menu ui->statusBar->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(ui->statusBar, &QStatusBar::customContextMenuRequested, [=](QPoint pos) { + QObject::connect(ui->statusBar, &QStatusBar::customContextMenuRequested, [=](QPoint pos) + { auto msg = ui->statusBar->currentMessage(); QMenu menu(this); @@ -406,8 +444,7 @@ void MainWindow::setupStatusBar() { rpc->refresh(true); }); QPoint gpos(mapToGlobal(pos).x(), mapToGlobal(pos).y() + this->height() - ui->statusBar->height()); - menu.exec(gpos); - }); + menu.exec(gpos); }); statusLabel = new QLabel(); ui->statusBar->addPermanentWidget(statusLabel); @@ -416,9 +453,11 @@ void MainWindow::setupStatusBar() { ui->statusBar->addPermanentWidget(statusIcon); } -void MainWindow::setupSettingsModal() { +void MainWindow::setupSettingsModal() +{ // Set up File -> Settings action - QObject::connect(ui->actionSettings, &QAction::triggered, [=]() { + QObject::connect(ui->actionSettings, &QAction::triggered, [=]() + { QDialog settingsDialog(this); //Ui_Settings settings; settings.setupUi(&settingsDialog); @@ -809,15 +848,17 @@ void MainWindow::setupSettingsModal() { QMessageBox::information(this, tr("Restart SilentDragon"), desc, QMessageBox::Ok); QTimer::singleShot(1, [=]() { this->close(); }); } - } - }); + } }); } -void MainWindow::addressBook() { +void MainWindow::addressBook() +{ // Check to see if there is a target. QRegularExpression re("Address[0-9]+", QRegularExpression::CaseInsensitiveOption); - for (auto target: ui->sendToWidgets->findChildren(re)) { - if (target->hasFocus()) { + for (auto target : ui->sendToWidgets->findChildren(re)) + { + if (target->hasFocus()) + { AddressBook::open(this, target); return; } @@ -827,30 +868,36 @@ void MainWindow::addressBook() { AddressBook::open(this); } -void MainWindow::telegram() { +void MainWindow::telegram() +{ QString url = "https://hush.is/tg"; - if (isdragonx) { - url = "https://dragonx.is/tg"; + if (isHSC) + { + url = QString::fromStdString(HSC_website); } QDesktopServices::openUrl(QUrl(url)); } -void MainWindow::reportbug() { +void MainWindow::reportbug() +{ // dragonx doesn't have it's own support, for now QString url = "https://hush.is/tg_support"; QDesktopServices::openUrl(QUrl(url)); } -void MainWindow::website() { +void MainWindow::website() +{ QString url = "https://hush.is"; - if (isdragonx) { - url = "https://dragonx.is"; + if (isHSC) + { + url = QString::fromStdString(HSC_website); } QDesktopServices::openUrl(QUrl(url)); } // Validate an address -void MainWindow::validateAddress() { +void MainWindow::validateAddress() +{ // Make sure everything is up and running if (!getRPC() || !getRPC()->getConnection()) return; @@ -858,12 +905,13 @@ void MainWindow::validateAddress() { // First thing is ask the user for an address bool ok; auto address = QInputDialog::getText(this, tr("Enter Address to validate"), - tr("Transparent or Shielded Address:") + QString(" ").repeated(140), // Pad the label so the dialog box is wide enough - QLineEdit::Normal, "", &ok); + tr("Transparent or Shielded Address:") + QString(" ").repeated(140), // Pad the label so the dialog box is wide enough + QLineEdit::Normal, "", &ok); if (!ok) return; - getRPC()->validateAddress(address, [=] (QJsonValue props) { + getRPC()->validateAddress(address, [=](QJsonValue props) + { QDialog d(this); Ui_ValidateAddress va; va.setupUi(&d); @@ -893,18 +941,19 @@ void MainWindow::validateAddress() { ValidateAddressesModel model(va.tblProps, propsList); va.tblProps->setModel(&model); - d.exec(); - }); - + d.exec(); }); } -void MainWindow::doImport(QList* keys) { - if (rpc->getConnection() == nullptr) { +void MainWindow::doImport(QList *keys) +{ + if (rpc->getConnection() == nullptr) + { // No connection, just return return; } - if (keys->isEmpty()) { + if (keys->isEmpty()) + { delete keys; ui->statusBar->showMessage(tr("Private key import rescan finished")); return; @@ -916,17 +965,22 @@ void MainWindow::doImport(QList* keys) { bool rescan = keys->isEmpty(); if (key.startsWith("SK") || - key.startsWith("secret")) { // Z key - rpc->importZPrivKey(key, rescan, [=] (auto) { this->doImport(keys); }); - } else { - rpc->importTPrivKey(key, rescan, [=] (auto) { this->doImport(keys); }); + key.startsWith("secret")) + { // Z key + rpc->importZPrivKey(key, rescan, [=](auto) + { this->doImport(keys); }); + } + else + { + rpc->importTPrivKey(key, rescan, [=](auto) + { this->doImport(keys); }); } } - // Callback invoked when the RPC has finished loading all the balances, and the UI // is now ready to send transactions. -void MainWindow::balancesReady() { +void MainWindow::balancesReady() +{ // First-time check if (uiPaymentsReady) return; @@ -936,18 +990,20 @@ void MainWindow::balancesReady() { // There is a pending URI payment (from the command line, or from a secondary instance), // process it. - if (!pendingURIPayment.isEmpty()) { + if (!pendingURIPayment.isEmpty()) + { qDebug() << "Paying hush URI"; payHushURI(pendingURIPayment); pendingURIPayment = ""; } - } // Event filter for MacOS specific handling of payment URIs -bool MainWindow::eventFilter(QObject *object, QEvent *event) { - if (event->type() == QEvent::FileOpen) { - QFileOpenEvent *fileEvent = static_cast(event); +bool MainWindow::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FileOpen) + { + QFileOpenEvent *fileEvent = static_cast(event); if (!fileEvent->url().isEmpty()) payHushURI(fileEvent->url().toString()); @@ -957,26 +1013,30 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) { return QObject::eventFilter(object, event); } - // Pay the Hush URI by showing a confirmation window. If the URI parameter is empty, the UI // will prompt for one. If the myAddr is empty, then the default from address is used to send // the transaction. -void MainWindow::payHushURI(QString uri, QString myAddr) { +void MainWindow::payHushURI(QString uri, QString myAddr) +{ // If the Payments UI is not ready (i.e, all balances have not loaded), defer the payment URI - if (!uiPaymentsReady) { + if (!uiPaymentsReady) + { qDebug() << "Payment UI not ready, waiting for UI to pay URI"; pendingURIPayment = uri; return; } // If there was no URI passed, ask the user for one. - if (uri.isEmpty()) { + if (uri.isEmpty()) + { uri = QInputDialog::getText(this, tr("Paste HUSH URI"), - "HUSH URI" + QString(" ").repeated(180)); - if(isdragonx) { - uri = QInputDialog::getText(this, tr("Paste DRGX URI"), - "DRGX URI" + QString(" ").repeated(180)); - } + "HUSH URI" + QString(" ").repeated(180)); + if (isHSC) + { + std::string str1 = "Paste " + HSC_ac_name + " URI"; + std::string str2 = HSC_ticker + " URI"; + uri = QInputDialog::getText(this, tr((str1.c_str())), (str2.c_str() + QString(" ").repeated(180))); + } } // If there's no URI, just exit @@ -986,20 +1046,27 @@ void MainWindow::payHushURI(QString uri, QString myAddr) { // Extract the address qDebug() << "Received URI " << uri; PaymentURI paymentInfo = Settings::parseURI(uri); - if (!paymentInfo.error.isEmpty()) { - if(isdragonx) { - QMessageBox::critical(this, tr("Error paying DragonX URI"), - tr("URI should be of the form 'drgx:?amt=x&memo=y") + "\n" + paymentInfo.error); - } else { + if (!paymentInfo.error.isEmpty()) + { + if (isHSC) + { + std::string payment_info_error = paymentInfo.error.toStdString(); + std::string payment_error = "Error paying " + HSC_name + " URI"; + std::string uri_error = "URI should be of the form '" + HSC_ticker + ":?amt=x&memo=y" + "\n" + payment_info_error; + QMessageBox::critical(this, tr(payment_error.c_str()), tr(uri_error.c_str())); + } + else + { QMessageBox::critical(this, tr("Error paying Hush URI"), - tr("URI should be of the form 'hush:?amt=x&memo=y") + "\n" + paymentInfo.error); + tr("URI should be of the form 'hush:?amt=x&memo=y") + "\n" + paymentInfo.error); } return; } // Now, set the fields on the send tab removeExtraAddresses(); - if (!myAddr.isEmpty()) { + if (!myAddr.isEmpty()) + { ui->inputsCombo->setCurrentText(myAddr); } @@ -1014,13 +1081,14 @@ void MainWindow::payHushURI(QString uri, QString myAddr) { // And click the send button if the amount is > 0, to validate everything. If everything is OK, it will show the confirm box // else, show the error message; - if (paymentInfo.amt > 0) { + if (paymentInfo.amt > 0) + { sendButton(); } } - -void MainWindow::importPrivKey() { +void MainWindow::importPrivKey() +{ QDialog d(this); Ui_PrivKey pui; pui.setupUi(&d); @@ -1028,27 +1096,27 @@ void MainWindow::importPrivKey() { pui.buttonBox->button(QDialogButtonBox::Save)->setVisible(false); pui.helpLbl->setText(QString() % - tr("Please paste your private keys here, one per line") % ".\n" % - tr("The keys will be imported into your connected Hush node")); + tr("Please paste your private keys here, one per line") % ".\n" % + tr("The keys will be imported into your connected Hush node")); - if (d.exec() == QDialog::Accepted && !pui.privKeyTxt->toPlainText().trimmed().isEmpty()) { + if (d.exec() == QDialog::Accepted && !pui.privKeyTxt->toPlainText().trimmed().isEmpty()) + { auto rawkeys = pui.privKeyTxt->toPlainText().trimmed().split("\n"); QList keysTmp; // Filter out all the empty keys. - std::copy_if(rawkeys.begin(), rawkeys.end(), std::back_inserter(keysTmp), [=] (auto key) { - return !key.startsWith("#") && !key.trimmed().isEmpty(); - }); + std::copy_if(rawkeys.begin(), rawkeys.end(), std::back_inserter(keysTmp), [=](auto key) + { return !key.startsWith("#") && !key.trimmed().isEmpty(); }); auto keys = new QList(); - std::transform(keysTmp.begin(), keysTmp.end(), std::back_inserter(*keys), [=](auto key) { - return key.trimmed().split(" ")[0]; - }); + std::transform(keysTmp.begin(), keysTmp.end(), std::back_inserter(*keys), [=](auto key) + { return key.trimmed().split(" ")[0]; }); // Special case. // Sometimes, when importing from a paperwallet or such, the key is split by newlines, and might have // been pasted like that. So check to see if the whole thing is one big private key - if (Settings::getInstance()->isValidSaplingPrivateKey(keys->join(""))) { + if (Settings::getInstance()->isValidSaplingPrivateKey(keys->join(""))) + { auto multiline = keys; keys = new QList(); keys->append(multiline->join("")); @@ -1056,54 +1124,59 @@ void MainWindow::importPrivKey() { } // Start the import. The function takes ownership of keys - QTimer::singleShot(1, [=]() {doImport(keys);}); + QTimer::singleShot(1, [=]() + { doImport(keys); }); // Show the dialog that keys will be imported. QMessageBox::information(this, - "Imported", tr("The keys were imported! It may take several minutes to rescan the blockchain. Until then, functionality may be limited"), - QMessageBox::Ok); + "Imported", tr("The keys were imported! It may take several minutes to rescan the blockchain. Until then, functionality may be limited"), + QMessageBox::Ok); } } /** * Export transaction history into a CSV file */ -void MainWindow::exportTransactions() { +void MainWindow::exportTransactions() +{ // First, get the export file name QString exportName = "hush-transactions-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".csv"; QUrl csvName = QFileDialog::getSaveFileUrl(this, - tr("Export transactions"), exportName, "CSV file (*.csv)"); + tr("Export transactions"), exportName, "CSV file (*.csv)"); if (csvName.isEmpty()) return; - if (!rpc->getTransactionsModel()->exportToCsv(csvName.toLocalFile())) { + if (!rpc->getTransactionsModel()->exportToCsv(csvName.toLocalFile())) + { QMessageBox::critical(this, tr("Error"), - tr("Error exporting transactions, file was not saved"), QMessageBox::Ok); + tr("Error exporting transactions, file was not saved"), QMessageBox::Ok); } } /** * Backup the wallet.dat file. This is kind of a hack, since it has to read from the filesystem rather than an RPC call * This might fail for various reasons - Remote hushd, non-standard locations, custom params passed to hushd, many others -*/ -void MainWindow::backupWalletDat() { + */ +void MainWindow::backupWalletDat() +{ if (!rpc->getConnection()) return; QDir hushdir(rpc->getConnection()->config->hushDir); QString backupDefaultName = "hush-wallet-backup-" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".dat"; - if (Settings::getInstance()->isTestnet()) { + if (Settings::getInstance()->isTestnet()) + { hushdir.cd("testnet3"); backupDefaultName = "testnet-" + backupDefaultName; } QFile wallet(hushdir.filePath("wallet.dat")); - if (!wallet.exists()) { - QMessageBox::critical(this, tr("No wallet.dat"), tr("Couldn't find the wallet.dat on this computer") + "\n" + - tr("You need to back it up from the machine hushd is running on"), QMessageBox::Ok); + if (!wallet.exists()) + { + QMessageBox::critical(this, tr("No wallet.dat"), tr("Couldn't find the wallet.dat on this computer") + "\n" + tr("You need to back it up from the machine hushd is running on"), QMessageBox::Ok); return; } @@ -1111,17 +1184,19 @@ void MainWindow::backupWalletDat() { if (backupName.isEmpty()) return; - if (!wallet.copy(backupName.toLocalFile())) { - QMessageBox::critical(this, tr("Couldn't backup"), tr("Couldn't backup the wallet.dat file.") + - tr("You need to back it up manually."), QMessageBox::Ok); + if (!wallet.copy(backupName.toLocalFile())) + { + QMessageBox::critical(this, tr("Couldn't backup"), tr("Couldn't backup the wallet.dat file.") + tr("You need to back it up manually."), QMessageBox::Ok); } } -void MainWindow::exportAllKeys() { +void MainWindow::exportAllKeys() +{ exportKeys(""); } -void MainWindow::getViewKey(QString addr) { +void MainWindow::getViewKey(QString addr) +{ QDialog d(this); Ui_ViewKey vui; vui.setupUi(&d); @@ -1141,9 +1216,10 @@ void MainWindow::getViewKey(QString addr) { vui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); vui.buttonBox->button(QDialogButtonBox::Ok)->setVisible(false); - bool allKeys = false; //addr.isEmpty() ? true : false; + bool allKeys = false; // addr.isEmpty() ? true : false; // Wire up save button - QObject::connect(vui.buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=] () { + QObject::connect(vui.buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=]() + { QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), allKeys ? "hush-all-viewkeys.txt" : "hush-viewkey.txt"); QFile file(fileName); @@ -1153,17 +1229,19 @@ void MainWindow::getViewKey(QString addr) { } QTextStream out(&file); // TODO: Output in address, viewkey CSV format? - out << vui.viewKeyTxt->toPlainText(); - }); + out << vui.viewKeyTxt->toPlainText(); }); auto isDialogAlive = std::make_shared(true); - auto fnUpdateUIWithKeys = [=](QList> viewKeys) { + auto fnUpdateUIWithKeys = [=](QList> viewKeys) + { // Check to see if we are still showing. - if (! *(isDialogAlive.get()) ) return; + if (!*(isDialogAlive.get())) + return; QString allKeysTxt; - for (auto keypair : viewKeys) { + for (auto keypair : viewKeys) + { allKeysTxt = allKeysTxt % keypair.second % " # addr=" % keypair.first % "\n"; } @@ -1171,7 +1249,8 @@ void MainWindow::getViewKey(QString addr) { vui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true); }; - auto fnAddKey = [=](QJsonValue key) { + auto fnAddKey = [=](QJsonValue key) + { QList> singleAddrKey; singleAddrKey.push_back(QPair(addr, key.toString())); fnUpdateUIWithKeys(singleAddrKey); @@ -1183,7 +1262,8 @@ void MainWindow::getViewKey(QString addr) { *isDialogAlive = false; } -void MainWindow::getQRCode(QString addr) { +void MainWindow::getQRCode(QString addr) +{ QDialog d(this); Ui_QRCode qrui; qrui.setupUi(&d); @@ -1198,7 +1278,8 @@ void MainWindow::getQRCode(QString addr) { auto isDialogAlive = std::make_shared(true); // Connect and handle Save button - QObject::connect(qrui.saveQRCodeBtn, &QPushButton::clicked, [&] () { + QObject::connect(qrui.saveQRCodeBtn, &QPushButton::clicked, [&]() + { qDebug() << "Save QR Code clicked"; QString fileName = QFileDialog::getSaveFileName(this, tr("Save QR Code to file"), "", @@ -1212,14 +1293,14 @@ void MainWindow::getQRCode(QString addr) { qrui.qrcodeDisplayAddr->grab().save(fileName, "png", -1); statusBar()->showMessage(tr("QR code saved"), 3000); d.close(); - } - }); + } }); d.exec(); *isDialogAlive = false; } -void MainWindow::exportKeys(QString addr) { +void MainWindow::exportKeys(QString addr) +{ bool allKeys = addr.isEmpty() ? true : false; QDialog d(this); @@ -1247,7 +1328,8 @@ void MainWindow::exportKeys(QString addr) { pui.buttonBox->button(QDialogButtonBox::Ok)->setVisible(false); // Wire up save button - QObject::connect(pui.buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=] () { + QObject::connect(pui.buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=]() + { QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), allKeys ? "hush-all-privatekeys.txt" : "hush-privatekey.txt"); QFile file(fileName); @@ -1256,18 +1338,20 @@ void MainWindow::exportKeys(QString addr) { return; } QTextStream out(&file); - out << pui.privKeyTxt->toPlainText(); - }); + out << pui.privKeyTxt->toPlainText(); }); // Call the API auto isDialogAlive = std::make_shared(true); - auto fnUpdateUIWithKeys = [=](QList> privKeys) { + auto fnUpdateUIWithKeys = [=](QList> privKeys) + { // Check to see if we are still showing. - if (! *(isDialogAlive.get()) ) return; + if (!*(isDialogAlive.get())) + return; QString allKeysTxt; - for (auto keypair : privKeys) { + for (auto keypair : privKeys) + { allKeysTxt = allKeysTxt % keypair.second % " # addr=" % keypair.first % "\n"; } @@ -1275,19 +1359,25 @@ void MainWindow::exportKeys(QString addr) { pui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true); }; - if (allKeys) { + if (allKeys) + { rpc->getAllPrivKeys(fnUpdateUIWithKeys); } - else { - auto fnAddKey = [=](QJsonValue key) { + else + { + auto fnAddKey = [=](QJsonValue key) + { QList> singleAddrKey; singleAddrKey.push_back(QPair(addr, key.toString())); fnUpdateUIWithKeys(singleAddrKey); }; - if (Settings::getInstance()->isZAddress(addr)) { + if (Settings::getInstance()->isZAddress(addr)) + { rpc->getZPrivKey(addr, fnAddKey); - } else { + } + else + { rpc->getTPrivKey(addr, fnAddKey); } } @@ -1296,29 +1386,35 @@ void MainWindow::exportKeys(QString addr) { *isDialogAlive = false; } -void MainWindow::setupBalancesTab() { +void MainWindow::setupBalancesTab() +{ ui->unconfirmedWarning->setVisible(false); // Double click on balances table - auto fnDoSendFrom = [=](const QString& addr, const QString& to = QString(), bool sendMax = false) { + auto fnDoSendFrom = [=](const QString &addr, const QString &to = QString(), bool sendMax = false) + { // Find the inputs combo - for (int i = 0; i < ui->inputsCombo->count(); i++) { + for (int i = 0; i < ui->inputsCombo->count(); i++) + { auto inputComboAddress = ui->inputsCombo->itemText(i); - if (inputComboAddress.startsWith(addr)) { + if (inputComboAddress.startsWith(addr)) + { ui->inputsCombo->setCurrentIndex(i); break; } } // If there's a to address, add that as well - if (!to.isEmpty()) { + if (!to.isEmpty()) + { // Remember to clear any existing address fields, because we are creating a new transaction. this->removeExtraAddresses(); ui->Address1->setText(to); } // See if max button has to be checked - if (sendMax) { + if (sendMax) + { ui->Max1->setChecked(true); } @@ -1327,16 +1423,17 @@ void MainWindow::setupBalancesTab() { }; // Double click opens up memo if one exists - QObject::connect(ui->balancesTable, &QTableView::doubleClicked, [=](auto index) { + QObject::connect(ui->balancesTable, &QTableView::doubleClicked, [=](auto index) + { index = index.sibling(index.row(), 0); auto addr = AddressBook::addressFromAddressLabel(ui->balancesTable->model()->data(index).toString()); - fnDoSendFrom(addr); - }); + fnDoSendFrom(addr); }); // Setup context menu on balances tab ui->balancesTable->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(ui->balancesTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) { + QObject::connect(ui->balancesTable, &QTableView::customContextMenuRequested, [=](QPoint pos) + { QModelIndex index = ui->balancesTable->indexAt(pos); if (index.row() < 0) return; @@ -1515,28 +1612,33 @@ void MainWindow::setupBalancesTab() { }); } - menu.exec(ui->balancesTable->viewport()->mapToGlobal(pos)); - }); + menu.exec(ui->balancesTable->viewport()->mapToGlobal(pos)); }); } -QString peer2ip(QString peer) { +QString peer2ip(QString peer) +{ QString ip = ""; - if(peer.contains("[")) { + if (peer.contains("[")) + { // this is actually ipv6, grab it all except the port auto parts = peer.split(":"); - parts[8]=""; // remove port + parts[8] = ""; // remove port peer = parts.join(":"); peer.chop(1); // remove trailing : - } else { - ip = peer.split(":")[0]; + } + else + { + ip = peer.split(":")[0]; } return ip; } -void MainWindow::setupMiningTab() { +void MainWindow::setupMiningTab() +{ DEBUG("setting up mining tab"); - //TODO: for other HSC's, look at getinfo.ac_algo == randomx - if(isdragonx) { + // TODO: for other HSC's, look at getinfo.ac_algo == randomx + if (isHSC) + { int hwc = std::thread::hardware_concurrency(); DEBUG("hardware concurrency = " << hwc); auto tab = new QWidget(); @@ -1545,13 +1647,13 @@ void MainWindow::setupMiningTab() { 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 label7 = new QLabel(tr("Select the number of threads to mine with:"), tab); + // 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 label7 = new QLabel(tr("Select the number of threads to mine with:"), tab); label2->setObjectName("mininglabel2"); label3->setObjectName("mininglabel3"); label4->setObjectName("mininglabel4"); @@ -1559,14 +1661,15 @@ void MainWindow::setupMiningTab() { label6->setObjectName("mininglabel6"); label7->setObjectName("mininglabel7"); - auto combo = new QComboBox(tab); + auto combo = new QComboBox(tab); combo->setObjectName("genproclimit"); - auto threadStr = tr("thread"); + auto threadStr = tr("thread"); auto threadsStr = tr("threads"); // 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) % " " % (i+1==1 ? threadStr : threadsStr)); + for (int i = 0; i < hwc / 2; i++) + { + combo->insertItem(i, QString::number(i + 1) % " " % (i + 1 == 1 ? threadStr : threadsStr)); } QFont font; @@ -1580,11 +1683,11 @@ void MainWindow::setupMiningTab() { label6->setFont(font); label7->setFont(font); label7->setAlignment(Qt::AlignHCenter); - 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); + 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"); @@ -1600,14 +1703,15 @@ void MainWindow::setupMiningTab() { lcd5->display(QString("0")); lcd5->setObjectName("miningthreads"); - auto button1 = new QPushButton(tr("Start Mining"), tab); - auto button2 = new QPushButton(tr("Stop Mining"), tab); + 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"); - connect(combo, QOverload::of(&QComboBox::currentIndexChanged), [=](int index){ + connect(combo, QOverload::of(&QComboBox::currentIndexChanged), [=](int index) + { auto button1 = ui->tabWidget->findChild("startmining"); if(button1 != nullptr) { DEBUG("found start mining button and enabled=" << button1->isEnabled() ); @@ -1632,10 +1736,10 @@ void MainWindow::setupMiningTab() { auto miningthreads = ui->tabWidget->findChild("miningthreads"); miningthreads->display(QString::number(threads)); // miningthreads DEBUG("updated mining thread count to " << QString(threads) ); - }); - }); + }); }); - QObject::connect(button1, &QPushButton::clicked, [&] () { + QObject::connect(button1, &QPushButton::clicked, [&]() + { DEBUG("START MINING"); int threads = 1; @@ -1654,10 +1758,10 @@ void MainWindow::setupMiningTab() { // miningthreads->display(QString(threads)); // miningthreads miningthreads->display(QString::number(threads)); // miningthreads DEBUG("updated mining thread count to " << QString(threads) ); - }); - }); + }); }); - QObject::connect(button2, &QPushButton::clicked, [&] () { + QObject::connect(button2, &QPushButton::clicked, [&]() + { DEBUG("STOP MINING"); ui->statusBar->showMessage(tr("Stopping mining..."), 5000); rpc->stopGenerate(0, [=] (QJsonValue response){ @@ -1668,9 +1772,7 @@ void MainWindow::setupMiningTab() { // 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 @@ -1680,7 +1782,7 @@ void MainWindow::setupMiningTab() { // gridLayout->addWidget(radio, 0, 0); // gridLayout->addWidget(label1, 0, 1, Qt::AlignLeft); gridLayout->addWidget(button1, 0, 0); - //gridLayout->addWidget(label1, 0, 1); + // gridLayout->addWidget(label1, 0, 1); gridLayout->addWidget(label7, 0, 1); gridLayout->addWidget(button2, 1, 0); gridLayout->addWidget(combo, 1, 1); @@ -1694,18 +1796,22 @@ void MainWindow::setupMiningTab() { gridLayout->addWidget(lcd3, 5, 1); gridLayout->addWidget(label6, 6, 0); gridLayout->addWidget(lcd4, 6, 1); - } else { + } + else + { // Mining tab currently only enabled for DragonX } } -void MainWindow::setupPeersTab() { +void MainWindow::setupPeersTab() +{ qDebug() << __FUNCTION__; // Set up context menu on peers tab ui->peersTable->setContextMenuPolicy(Qt::CustomContextMenu); ui->bannedPeersTable->setContextMenuPolicy(Qt::CustomContextMenu); // Table right click - QObject::connect(ui->bannedPeersTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) { + QObject::connect(ui->bannedPeersTable, &QTableView::customContextMenuRequested, [=](QPoint pos) + { QModelIndex index = ui->bannedPeersTable->indexAt(pos); if (index.row() < 0) return; @@ -1776,11 +1882,11 @@ void MainWindow::setupPeersTab() { }); } - menu.exec(ui->bannedPeersTable->viewport()->mapToGlobal(pos)); - }); + menu.exec(ui->bannedPeersTable->viewport()->mapToGlobal(pos)); }); // Table right click - QObject::connect(ui->peersTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) { + QObject::connect(ui->peersTable, &QTableView::customContextMenuRequested, [=](QPoint pos) + { QModelIndex index = ui->peersTable->indexAt(pos); if (index.row() < 0) return; @@ -1845,8 +1951,7 @@ void MainWindow::setupPeersTab() { }); }); - menu.exec(ui->peersTable->viewport()->mapToGlobal(pos)); - }); + menu.exec(ui->peersTable->viewport()->mapToGlobal(pos)); }); /* //grep 'BAN THRESHOLD EXCEEDED' ~/.hush/HUSH3/debug.log @@ -1875,13 +1980,13 @@ void MainWindow::setupPeersTab() { } */ - //ui->recentlyBannedPeers = "Could not open " + debuglog; - + // ui->recentlyBannedPeers = "Could not open " + debuglog; } -void MainWindow::setupHushTab() { +void MainWindow::setupHushTab() +{ QPixmap image(":/img/hushdlogo.png"); - ui->hushlogo->setBasePixmap( image ); // image.scaled(600,600, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation ) ); + ui->hushlogo->setBasePixmap(image); // image.scaled(600,600, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation ) ); } /* void MainWindow::setupChatTab() { @@ -1916,19 +2021,22 @@ void MainWindow::setupChatTab() { } */ -void MainWindow::setupMarketTab() { +void MainWindow::setupMarketTab() +{ qDebug() << "Setting up market tab"; - auto s = Settings::getInstance(); + auto s = Settings::getInstance(); auto ticker = s->get_currency_name(); - ui->volume->setText(QString::number((double) s->get_volume("HUSH") ,'f',8) + " HUSH"); - ui->volumeLocal->setText(QString::number((double) s->get_volume(ticker) ,'f',8) + " " + ticker); - ui->volumeBTC->setText(QString::number((double) s->get_volume("BTC") ,'f',8) + " BTC"); + ui->volume->setText(QString::number((double)s->get_volume("HUSH"), 'f', 8) + " HUSH"); + ui->volumeLocal->setText(QString::number((double)s->get_volume(ticker), 'f', 8) + " " + ticker); + ui->volumeBTC->setText(QString::number((double)s->get_volume("BTC"), 'f', 8) + " BTC"); } -void MainWindow::setupTransactionsTab() { +void MainWindow::setupTransactionsTab() +{ // Double click opens up memo if one exists - QObject::connect(ui->transactionsTable, &QTableView::doubleClicked, [=] (auto index) { + QObject::connect(ui->transactionsTable, &QTableView::doubleClicked, [=](auto index) + { auto txModel = dynamic_cast(ui->transactionsTable->model()); QString memo = txModel->getMemo(index.row()); @@ -1979,14 +2087,14 @@ void MainWindow::setupTransactionsTab() { mb.exec(); } } - } - }); + } }); // Set up context menu on transactions tab ui->transactionsTable->setContextMenuPolicy(Qt::CustomContextMenu); // Table right click - QObject::connect(ui->transactionsTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) { + QObject::connect(ui->transactionsTable, &QTableView::customContextMenuRequested, [=](QPoint pos) + { QModelIndex index = ui->transactionsTable->indexAt(pos); if (index.row() < 0) return; @@ -2051,7 +2159,8 @@ void MainWindow::setupTransactionsTab() { */ // Payment Request - if (!memo.isEmpty() && memo.startsWith(isdragonx ? "drgx:" : "hush:")) { + std::string ticker = HSC_ticker + ":"; + if (!memo.isEmpty() && memo.startsWith(isHSC ? (ticker.c_str()) : "hush:")) { menu.addAction(tr("View Payment Request"), [=] () { RequestDialog::showPaymentConfirmation(this, memo); }); @@ -2141,12 +2250,13 @@ void MainWindow::setupTransactionsTab() { } } - menu.exec(ui->transactionsTable->viewport()->mapToGlobal(pos)); - }); + menu.exec(ui->transactionsTable->viewport()->mapToGlobal(pos)); }); } -void MainWindow::addNewZaddr() { - rpc->newZaddr( [=] (QJsonValue reply) { +void MainWindow::addNewZaddr() +{ + rpc->newZaddr([=](QJsonValue reply) + { QString addr = reply.toString(); // Make sure the RPC class reloads the z-addrs for future use rpc->refreshAddresses(); @@ -2157,40 +2267,45 @@ void MainWindow::addNewZaddr() { ui->listReceiveAddresses->setCurrentIndex(0); ui->statusBar->showMessage(QString::fromStdString("Created new Sapling zaddr"), 10 * 1000); - } - }); + } }); } - // Adds z-addresses to the combo box. Technically, returns a // lambda, which can be connected to the appropriate signal -std::function MainWindow::addZAddrsToComboList(bool sapling) { - return [=] (bool checked) { - if (checked && this->rpc->getAllZAddresses() != nullptr) { +std::function MainWindow::addZAddrsToComboList(bool sapling) +{ + return [=](bool checked) + { + if (checked && this->rpc->getAllZAddresses() != nullptr) + { auto addrs = this->rpc->getAllZAddresses(); ui->listReceiveAddresses->clear(); - std::for_each(addrs->begin(), addrs->end(), [=] (auto addr) { + std::for_each(addrs->begin(), addrs->end(), [=](auto addr) + { if ( (sapling && Settings::getInstance()->isSaplingAddress(addr)) || (!sapling && !Settings::getInstance()->isSaplingAddress(addr))) { if (rpc->getAllBalances()) { auto bal = rpc->getAllBalances()->value(addr); ui->listReceiveAddresses->addItem(addr, bal); } - } - }); + } }); // If z-addrs are empty, then create a new one. - if (addrs->isEmpty()) { + if (addrs->isEmpty()) + { addNewZaddr(); } } }; } -void MainWindow::setupReceiveTab() { - auto addNewTAddr = [=] () { - rpc->newTaddr([=] (QJsonValue reply) { +void MainWindow::setupReceiveTab() +{ + auto addNewTAddr = [=]() + { + rpc->newTaddr([=](QJsonValue reply) + { qDebug() << "New addr button clicked"; QString addr = reply.toString(); // Make sure the RPC class reloads the t-addrs for future use @@ -2202,23 +2317,23 @@ void MainWindow::setupReceiveTab() { ui->listReceiveAddresses->setCurrentIndex(0); ui->statusBar->showMessage(tr("Created new t-Addr"), 10 * 1000); - } - }); + } }); }; // Connect t-addr radio button - QObject::connect(ui->rdioTAddr, &QRadioButton::toggled, [=] (bool checked) { + QObject::connect(ui->rdioTAddr, &QRadioButton::toggled, [=](bool checked) + { qDebug() << "taddr radio toggled"; if (checked && this->rpc->getUTXOs() != nullptr) { updateTAddrCombo(checked); } // Toggle the "View all addresses" button as well - ui->btnViewAllAddresses->setVisible(checked); - }); + ui->btnViewAllAddresses->setVisible(checked); }); // View all addresses goes to "View all private keys" - QObject::connect(ui->btnViewAllAddresses, &QPushButton::clicked, [=] () { + QObject::connect(ui->btnViewAllAddresses, &QPushButton::clicked, [=]() + { // If there's no RPC, return if (!getRPC()) return; @@ -2256,13 +2371,13 @@ void MainWindow::setupReceiveTab() { menu.exec(viewaddrs.tblAddresses->viewport()->mapToGlobal(pos)); }); - d.exec(); - }); + d.exec(); }); QObject::connect(ui->rdioZSAddr, &QRadioButton::toggled, addZAddrsToComboList(true)); // Explicitly get new address button. - QObject::connect(ui->btnReceiveNewAddr, &QPushButton::clicked, [=] () { + QObject::connect(ui->btnReceiveNewAddr, &QPushButton::clicked, [=]() + { if (!rpc->getConnection()) return; @@ -2270,11 +2385,11 @@ void MainWindow::setupReceiveTab() { addNewZaddr(); } else if (ui->rdioTAddr->isChecked()) { addNewTAddr(); - } - }); + } }); // Focus enter for the Receive Tab - QObject::connect(ui->tabWidget, &QTabWidget::currentChanged, [=] (int tab) { + QObject::connect(ui->tabWidget, &QTabWidget::currentChanged, [=](int tab) + { if (tab == 2) { // Switched to receive tab, select the z-addr radio button ui->rdioZSAddr->setChecked(true); @@ -2282,50 +2397,55 @@ void MainWindow::setupReceiveTab() { // And then select the first one ui->listReceiveAddresses->setCurrentIndex(0); - } - }); + } }); // Validator for label - QRegExpValidator* v = new QRegExpValidator(QRegExp(Settings::labelRegExp), ui->rcvLabel); + QRegExpValidator *v = new QRegExpValidator(QRegExp(Settings::labelRegExp), ui->rcvLabel); ui->rcvLabel->setValidator(v); // Select item in address list QObject::connect(ui->listReceiveAddresses, - QOverload::of(&QComboBox::currentIndexChanged), [=] (int index) { - QString addr = ui->listReceiveAddresses->itemText(index); - if (addr.isEmpty()) { - // Draw empty stuff - - ui->rcvLabel->clear(); - ui->rcvBal->clear(); - ui->txtReceive->clear(); - ui->qrcodeDisplay->clear(); - return; - } - - auto label = AddressBook::getInstance()->getLabelForAddress(addr); - if (label.isEmpty()) { - ui->rcvUpdateLabel->setText("Add Label"); - } - else { - ui->rcvUpdateLabel->setText("Update Label"); - } - - ui->rcvLabel->setText(label); - ui->rcvBal->setText(Settings::getHUSHUSDDisplayFormat(rpc->getAllBalances()->value(addr))); - ui->txtReceive->setPlainText(addr); - ui->qrcodeDisplay->setQrcodeString(addr); - - if (rpc->getUsedAddresses()->value(addr, false)) { - ui->rcvBal->setToolTip(tr("Address has been previously used")); - } else { - ui->rcvBal->setToolTip(tr("Address is unused")); - } - - }); + QOverload::of(&QComboBox::currentIndexChanged), [=](int index) + { + QString addr = ui->listReceiveAddresses->itemText(index); + if (addr.isEmpty()) + { + // Draw empty stuff + + ui->rcvLabel->clear(); + ui->rcvBal->clear(); + ui->txtReceive->clear(); + ui->qrcodeDisplay->clear(); + return; + } + + auto label = AddressBook::getInstance()->getLabelForAddress(addr); + if (label.isEmpty()) + { + ui->rcvUpdateLabel->setText("Add Label"); + } + else + { + ui->rcvUpdateLabel->setText("Update Label"); + } + + ui->rcvLabel->setText(label); + ui->rcvBal->setText(Settings::getHUSHUSDDisplayFormat(rpc->getAllBalances()->value(addr))); + ui->txtReceive->setPlainText(addr); + ui->qrcodeDisplay->setQrcodeString(addr); + + if (rpc->getUsedAddresses()->value(addr, false)) + { + ui->rcvBal->setToolTip(tr("Address has been previously used")); + } + else + { + ui->rcvBal->setToolTip(tr("Address is unused")); + } }); // Receive tab add/update label - QObject::connect(ui->rcvUpdateLabel, &QPushButton::clicked, [=]() { + QObject::connect(ui->rcvUpdateLabel, &QPushButton::clicked, [=]() + { QString addr = ui->listReceiveAddresses->currentText(); if (addr.isEmpty()) return; @@ -2357,42 +2477,45 @@ void MainWindow::setupReceiveTab() { // Show the user feedback if (!info.isEmpty()) { QMessageBox::information(this, "Label", info, QMessageBox::Ok); - } - }); + } }); // Receive Export Key - QObject::connect(ui->exportKey, &QPushButton::clicked, [=]() { + QObject::connect(ui->exportKey, &QPushButton::clicked, [=]() + { QString addr = ui->listReceiveAddresses->currentText(); if (addr.isEmpty()) return; - this->exportKeys(addr); - }); + this->exportKeys(addr); }); } - -void MainWindow::updateTAddrCombo(bool checked) { - if (checked) { +void MainWindow::updateTAddrCombo(bool checked) +{ + if (checked) + { auto utxos = this->rpc->getUTXOs(); ui->listReceiveAddresses->clear(); - std::for_each(utxos->begin(), utxos->end(), [=](auto& utxo) { + std::for_each(utxos->begin(), utxos->end(), [=](auto &utxo) + { auto addr = utxo.address; if (addr.startsWith("R") && ui->listReceiveAddresses->findText(addr) < 0) { auto bal = rpc->getAllBalances()->value(addr); ui->listReceiveAddresses->addItem(addr, bal); - } - }); + } }); } }; // Updates the labels everywhere on the UI. Call this after the labels have been updated -void MainWindow::updateLabels() { +void MainWindow::updateLabels() +{ // Update the Receive tab - if (ui->rdioTAddr->isChecked()) { + if (ui->rdioTAddr->isChecked()) + { updateTAddrCombo(true); } - else { + else + { addZAddrsToComboList(ui->rdioZSAddr->isChecked())(true); } @@ -2403,7 +2526,7 @@ void MainWindow::updateLabels() { updateLabelsAutoComplete(); } -void MainWindow::slot_change_currency(const QString& currency_name) +void MainWindow::slot_change_currency(const QString ¤cy_name) { qDebug() << __func__ << ": " << currency_name; Settings::getInstance()->set_currency_name(currency_name); @@ -2412,53 +2535,65 @@ void MainWindow::slot_change_currency(const QString& currency_name) // Include currency QString saved_currency_name; - try { - saved_currency_name = Settings::getInstance()->get_currency_name(); - } catch (const std::exception& e) { + try + { + saved_currency_name = Settings::getInstance()->get_currency_name(); + } + catch (const std::exception &e) + { qDebug() << QString("Ignoring currency change Exception! : "); saved_currency_name = "BTC"; } } -void MainWindow::slot_change_theme(QString& theme_name) +void MainWindow::slot_change_theme(QString &theme_name) { qDebug() << __func__ << ": theme_name=" << theme_name; if (theme_name == "dark" || theme_name == "default" || theme_name == "light" || - theme_name == "midnight" || theme_name == "blue" || theme_name == "dragonx") { + theme_name == "midnight" || theme_name == "blue" || theme_name == (QString::fromStdString(HSC_default_theme))) + { Settings::getInstance()->set_theme_name(theme_name); - } else { + } + else + { qDebug() << __func__ << ": ignoring invalid theme_name=" << theme_name; Settings::getInstance()->set_theme_name("dark"); } // Include css QString saved_theme_name; - try { - saved_theme_name = Settings::getInstance()->get_theme_name(); - } catch (const std::exception& e) { + try + { + saved_theme_name = Settings::getInstance()->get_theme_name(); + } + catch (const std::exception &e) + { qDebug() << QString("Ignoring theme change Exception! : "); - if(isdragonx){ - saved_theme_name = "dragonx"; - }else{ + if (isHSC) + { + saved_theme_name = QString::fromStdString(HSC_default_theme); + } + else + { saved_theme_name = "dark"; } } - QString filename = ":/css/" + saved_theme_name +".css"; + QString filename = ":/css/" + saved_theme_name + ".css"; QFile qFile(filename); qDebug() << __func__ << ": attempting to open filename=" << filename; if (qFile.open(QFile::ReadOnly)) { - QString styleSheet = QLatin1String(qFile.readAll()); - this->setStyleSheet(""); // reset styles - this->setStyleSheet(styleSheet); + QString styleSheet = QLatin1String(qFile.readAll()); + this->setStyleSheet(""); // reset styles + this->setStyleSheet(styleSheet); } - } -void MainWindow::rescanButtonClicked(int number) { +void MainWindow::rescanButtonClicked(int number) +{ qDebug() << __func__ << ": " << number; // Setup rescan dialog @@ -2471,11 +2606,12 @@ void MainWindow::rescanButtonClicked(int number) { rescanDialog.rescanBlockheight->setText("1"); // Add validator for block height - QRegExpValidator* heightValidator = new QRegExpValidator(QRegExp("\\d*"), this); + QRegExpValidator *heightValidator = new QRegExpValidator(QRegExp("\\d*"), this); rescanDialog.rescanBlockheight->setValidator(heightValidator); // Check if OK clicked - if (dialog.exec() == QDialog::Accepted) { + if (dialog.exec() == QDialog::Accepted) + { // Get submitted rescan height int rescanHeight = rescanDialog.rescanBlockheight->text().toInt(); qDebug() << __func__ << ": rescan height = " << rescanHeight; @@ -2488,17 +2624,15 @@ void MainWindow::rescanButtonClicked(int number) { if (modalWidget) modalWidget->close(); - // Call rescan RPC // TODO: This RPC might timeout, does the callback work correctly in that case? - rpc->rescan(rescanHeight, [=] (QJsonValue response){ + rpc->rescan(rescanHeight, [=](QJsonValue response) + { qDebug() << __func__ << ":rescanning finished" << response; - ui->statusBar->showMessage(tr("Rescanning finished"), 5000); - }); + ui->statusBar->showMessage(tr("Rescanning finished"), 5000); }); qDebug() << __func__ << ": force refresh of rescan data"; rpc->refreshRescan(); - } } @@ -2513,5 +2647,4 @@ MainWindow::~MainWindow() delete loadingMovie; delete logger; - } diff --git a/src/requestdialog.cpp b/src/requestdialog.cpp index 99d5eec..330383e 100644 --- a/src/requestdialog.cpp +++ b/src/requestdialog.cpp @@ -8,11 +8,13 @@ #include "rpc.h" #include "settings.h" #include "precompiled.h" -extern bool isdragonx; -RequestDialog::RequestDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::RequestDialog) +extern bool isHSC; +extern std::string HSC_ac_name; +extern std::string HSC_ticker; + +RequestDialog::RequestDialog(QWidget *parent) : QDialog(parent), + ui(new Ui::RequestDialog) { ui->setupUi(this); } @@ -22,7 +24,8 @@ RequestDialog::~RequestDialog() delete ui; } -void RequestDialog::setupDialog(MainWindow* main, QDialog* d, Ui_RequestDialog* req) { +void RequestDialog::setupDialog(MainWindow *main, QDialog *d, Ui_RequestDialog *req) +{ req->setupUi(d); Settings::saveRestore(d); @@ -33,9 +36,11 @@ void RequestDialog::setupDialog(MainWindow* main, QDialog* d, Ui_RequestDialog* if (!main || !main->getRPC() || !main->getRPC()->getAllZAddresses() || !main->getRPC()->getAllBalances()) return; - for (auto addr : *main->getRPC()->getAllZAddresses()) { + for (auto addr : *main->getRPC()->getAllZAddresses()) + { auto bal = main->getRPC()->getAllBalances()->value(addr); - if (Settings::getInstance()->isSaplingAddress(addr)) { + if (Settings::getInstance()->isSaplingAddress(addr)) + { req->cmbMyAddress->addItem(addr, bal); } } @@ -46,22 +51,28 @@ void RequestDialog::setupDialog(MainWindow* main, QDialog* d, Ui_RequestDialog* } // Static method that shows an incoming payment request and prompts the user to pay it -void RequestDialog::showPaymentConfirmation(MainWindow* main, QString paymentURI) { +void RequestDialog::showPaymentConfirmation(MainWindow *main, QString paymentURI) +{ PaymentURI payInfo = Settings::parseURI(paymentURI); - if (!payInfo.error.isEmpty()) { - if(isdragonx) { - QMessageBox::critical(main, tr("Error paying DRAGONX URI"), - tr("URI should be of the form 'drgx:?amt=x&memo=y") + "\n" + payInfo.error); - } else { - QMessageBox::critical(main, tr("Error paying HUSH URI"), - tr("URI should be of the form 'hush:?amt=x&memo=y") + "\n" + payInfo.error); + if (!payInfo.error.isEmpty()) + { + if (isHSC) + { + std::string error = "Error paying " + HSC_ac_name + " URI"; + std::string error2 = "URI should be of the form '" + HSC_ticker + ":?amt=x&memo=y"; + QMessageBox::critical(main, tr(error.c_str()), tr(error2.c_str()) + "\n" + payInfo.error); + } + else + { + QMessageBox::critical(main, tr("Error paying HUSH URI"), + tr("URI should be of the form 'hush:?amt=x&memo=y") + "\n" + payInfo.error); } return; } QDialog d(main); Ui_RequestDialog req; - setupDialog(main, &d, &req); + setupDialog(main, &d, &req); // In the view mode, all fields are read-only req.txtAmount->setReadOnly(true); @@ -86,13 +97,15 @@ void RequestDialog::showPaymentConfirmation(MainWindow* main, QString paymentURI req.lblHeader->setText(tr("You are paying a payment request. Your address will not be visible to the person requesting this payment.")); - if (d.exec() == QDialog::Accepted) { + if (d.exec() == QDialog::Accepted) + { main->payHushURI(paymentURI, req.cmbMyAddress->currentText()); } } // Static method that shows the request dialog -void RequestDialog::showRequestZcash(MainWindow* main) { +void RequestDialog::showRequestZcash(MainWindow *main) +{ QDialog d(main); Ui_RequestDialog req; @@ -100,7 +113,8 @@ void RequestDialog::showRequestZcash(MainWindow* main) { // Setup the Label completer for the Address req.txtFrom->setCompleter(main->getLabelCompleter()); - QObject::connect(req.txtFrom, &QLineEdit::textChanged, [=] (auto text) { + QObject::connect(req.txtFrom, &QLineEdit::textChanged, [=](auto text) + { auto addr = AddressBook::addressFromAddressLabel(text); if (!Settings::getInstance()->isSaplingAddress(addr)) { req.lblSaplingWarning->setText(tr("Can only request from Sapling addresses")); @@ -108,19 +122,16 @@ void RequestDialog::showRequestZcash(MainWindow* main) { } else { req.lblSaplingWarning->setText(""); req.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - } - }); + } }); // Wire up AddressBook button - QObject::connect(req.btnAddressBook, &QPushButton::clicked, [=] () { - AddressBook::open(main, req.txtFrom); - }); + QObject::connect(req.btnAddressBook, &QPushButton::clicked, [=]() + { AddressBook::open(main, req.txtFrom); }); // Amount textbox req.txtAmount->setValidator(main->getAmountValidator()); - QObject::connect(req.txtAmount, &QLineEdit::textChanged, [=] (auto text) { - req.txtAmountUSD->setText(Settings::getUSDFormat(text.toDouble())); - }); + QObject::connect(req.txtAmount, &QLineEdit::textChanged, [=](auto text) + { req.txtAmountUSD->setText(Settings::getUSDFormat(text.toDouble())); }); req.txtAmountUSD->setText(Settings::getUSDFormat(req.txtAmount->text().toDouble())); req.txtMemo->setAcceptButton(req.buttonBox->button(QDialogButtonBox::Ok)); @@ -129,15 +140,12 @@ void RequestDialog::showRequestZcash(MainWindow* main) { req.txtFrom->setFocus(); - if (d.exec() == QDialog::Accepted) { + if (d.exec() == QDialog::Accepted) + { // Construct a Hush Payment URI with the data and pay it immediately. - QString memoURI = (isdragonx ? "drgx:" : "hush:" ) + req.cmbMyAddress->currentText() - + "?amt=" + Settings::getDecimalString(req.txtAmount->text().toDouble()) - + "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText()); + QString memoURI = (isHSC ? (QString::fromStdString(HSC_ticker) + ":") : "hush:") + req.cmbMyAddress->currentText() + "?amt=" + Settings::getDecimalString(req.txtAmount->text().toDouble()) + "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText()); - QString sendURI = (isdragonx ? "drgx:" : "hush:" ) + AddressBook::addressFromAddressLabel(req.txtFrom->text()) - + "?amt=0.0001" - + "&memo=" + QUrl::toPercentEncoding(memoURI); + QString sendURI = (isHSC ? QString::fromStdString((HSC_ticker) + ":") : "hush:") + AddressBook::addressFromAddressLabel(req.txtFrom->text()) + "?amt=0.0001" + "&memo=" + QUrl::toPercentEncoding(memoURI); // If the disclosed address in the memo doesn't have a balance, it will automatically fallback to the default // sapling address diff --git a/src/rpc.cpp b/src/rpc.cpp index 84bed9d..2a7f665 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -8,16 +8,20 @@ #include "version.h" #include "sd.h" -extern bool isdragonx; +extern bool isHSC; +extern std::string App_title; +extern std::string HSC_name; -RPC::RPC(MainWindow* main) { +RPC::RPC(MainWindow *main) +{ auto cl = new ConnectionLoader(main, this); - // Execute the load connection async, so we can set up the rest of RPC properly. - QTimer::singleShot(1, [=]() { cl->loadConnection(); }); + // Execute the load connection async, so we can set up the rest of RPC properly. + QTimer::singleShot(1, [=]() + { cl->loadConnection(); }); this->main = main; - this->ui = main->ui; + this->ui = main->ui; // Setup balances table model balancesTableModel = new BalancesTableModel(main->ui->balancesTable); @@ -41,10 +45,10 @@ RPC::RPC(MainWindow* main) { // Set up timer to refresh Price priceTimer = new QTimer(main); - QObject::connect(priceTimer, &QTimer::timeout, [=]() { + QObject::connect(priceTimer, &QTimer::timeout, [=]() + { qDebug() << "Refreshing price data"; - refreshPrice(); - }); + refreshPrice(); }); priceTimer->start(Settings::priceRefreshSpeed); qDebug() << __func__ << ": started price refresh at speed=" << Settings::priceRefreshSpeed; @@ -54,42 +58,43 @@ RPC::RPC(MainWindow* main) { // PreciseTimer is needed for timely GUI updates rescanTimer->setTimerType(Qt::PreciseTimer); - QObject::connect(rescanTimer, &QTimer::timeout, [=]() { + QObject::connect(rescanTimer, &QTimer::timeout, [=]() + { qDebug() << "Refreshing rescan data"; - refreshRescan(); - }); + refreshRescan(); }); rescanTimer->start(10000); qDebug() << __func__ << ": started rescanTimer"; qDebug() << __func__ << ": Setting up a timer to refresh the UI every few seconds"; timer = new QTimer(main); - QObject::connect(timer, &QTimer::timeout, [=]() { + QObject::connect(timer, &QTimer::timeout, [=]() + { qDebug() << "Refreshing main UI"; - refresh(); - }); - timer->start(Settings::updateSpeed); + refresh(); }); + timer->start(Settings::updateSpeed); // PreciseTimer is needed for timely GUI updates timer->setTimerType(Qt::PreciseTimer); qDebug() << __func__ << ": Set up the timer to watch for tx status"; txTimer = new QTimer(main); - QObject::connect(txTimer, &QTimer::timeout, [=]() { + QObject::connect(txTimer, &QTimer::timeout, [=]() + { //qDebug() << "Watching tx status"; - watchTxStatus(); - }); + watchTxStatus(); }); qDebug() << __func__ << "Done settings up all timers"; usedAddresses = new QMap(); auto lang = Settings::getInstance()->get_language(); - qDebug() << __func__ << ": found lang="<< lang << " in config file"; + qDebug() << __func__ << ": found lang=" << lang << " in config file"; main->loadLanguage(lang); - qDebug() << __func__ << ": setting UI to lang="<< lang << " found in config file"; + qDebug() << __func__ << ": setting UI to lang=" << lang << " found in config file"; } -RPC::~RPC() { +RPC::~RPC() +{ qDebug() << "Deleting stuff"; delete timer; delete txTimer; @@ -109,23 +114,28 @@ RPC::~RPC() { delete conn; } -void RPC::setEHushd(std::shared_ptr p) { +void RPC::setEHushd(std::shared_ptr p) +{ ehushd = p; } -void RPC::pauseTimer() { +void RPC::pauseTimer() +{ qDebug() << "pausing main timer"; timer->stop(); } -void RPC::restartTimer() { +void RPC::restartTimer() +{ qDebug() << "restarting main timer"; timer->start(Settings::updateSpeed); } -// Called when a connection to hushd is available. -void RPC::setConnection(Connection* c) { - if (c == nullptr) return; +// Called when a connection to hushd is available. +void RPC::setConnection(Connection *c) +{ + if (c == nullptr) + return; delete conn; this->conn = c; @@ -139,356 +149,391 @@ void RPC::setConnection(Connection* c) { // Refresh the UI refreshPrice(); - //checkForUpdate(); + // checkForUpdate(); // Force update, because this might be coming from a settings update // where we need to immediately refresh refresh(true); } -QJsonValue RPC::makePayload(QString method, QString param, QString param2) { +QJsonValue RPC::makePayload(QString method, QString param, QString param2) +{ // qDebug() << __func__ << ": method=" << method << " payload=" << param << ", " << param2; QJsonObject payload = { {"jsonrpc", "1.0"}, - {"id", "42" }, - {"method", method }, - {"params", QJsonArray {param, param2}} - }; + {"id", "42"}, + {"method", method}, + {"params", QJsonArray{param, param2}}}; return payload; } -QJsonValue RPC::makePayload(QString method, QString param) { +QJsonValue RPC::makePayload(QString method, QString param) +{ // qDebug() << __func__ << ": method=" << method << " " << "payload=" << param; QJsonObject payload = { {"jsonrpc", "1.0"}, - {"id", "42" }, - {"method", method }, - {"params", QJsonArray {param}} - }; + {"id", "42"}, + {"method", method}, + {"params", QJsonArray{param}}}; return payload; } -QJsonValue RPC::makePayload(QString method, int param) { +QJsonValue RPC::makePayload(QString method, int param) +{ // qDebug() << __func__ << ": method=" << method << " payload=" << param; QJsonObject payload = { {"jsonrpc", "1.0"}, - {"id", "42" }, - {"method", method }, - {"params", QJsonArray{param}} - }; + {"id", "42"}, + {"method", method}, + {"params", QJsonArray{param}}}; return payload; } -QJsonValue RPC::makePayload(QString method) { +QJsonValue RPC::makePayload(QString method) +{ // qDebug() << __func__ << ": method=" << method << " with no payload"; QJsonObject payload = { {"jsonrpc", "1.0"}, - {"id", "42" }, - {"method", method }, + {"id", "42"}, + {"method", method}, }; return payload; } -//TODO: we can use listaddresses -void RPC::getTAddresses(const std::function& cb) { +// TODO: we can use listaddresses +void RPC::getTAddresses(const std::function &cb) +{ QString method = "getaddressesbyaccount"; QString params = ""; conn->doRPCWithDefaultErrorHandling(makePayload(method, ""), cb); } // full or partial rescan -void RPC::rescan(qint64 height, const std::function& cb) { +void RPC::rescan(qint64 height, const std::function &cb) +{ QString method = "rescan"; conn->doRPCWithDefaultErrorHandling(makePayload(method, height), cb); } // get rescan info -void RPC::getRescanInfo(const std::function& cb){ +void RPC::getRescanInfo(const std::function &cb) +{ QString method = "getrescaninfo"; // do not show an error in case getrescaninfo doesn't exist in this hushd conn->doRPCIgnoreError(makePayload(method), cb); } -void RPC::getnetworksolps(const std::function& cb){ +void RPC::getnetworksolps(const std::function &cb) +{ QString method = "getnetworksolps"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::getlocalsolps(const std::function& 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){ +void RPC::help(const std::function &cb) +{ QString method = "help"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } // add/remove a banned node. ip can include an optional netmask -void RPC::setban(QString ip, QString command, const std::function& cb) { +void RPC::setban(QString ip, QString command, const std::function &cb) +{ QString method = "setban"; conn->doRPCWithDefaultErrorHandling(makePayload(method, ip, command), cb); } -//unban all banned peer nodes -void RPC::clearBanned(const std::function& cb) { +// unban all banned peer nodes +void RPC::clearBanned(const std::function &cb) +{ QString method = "clearbanned"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::z_sweepstatus(const std::function& cb) { +void RPC::z_sweepstatus(const std::function &cb) +{ QString method = "z_sweepstatus"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::z_consolidationstatus(const std::function& cb) { +void RPC::z_consolidationstatus(const std::function &cb) +{ QString method = "z_consolidationstatus"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::getZAddresses(const std::function& cb) { +void RPC::getZAddresses(const std::function &cb) +{ QString method = "z_listaddresses"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::getTransparentUnspent(const std::function& cb) { +void RPC::getTransparentUnspent(const std::function &cb) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "listunspent"}, - {"params", QJsonArray {0}} // Get UTXOs with 0 confirmations as well. + {"params", QJsonArray{0}} // Get UTXOs with 0 confirmations as well. }; conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::getZUnspent(const std::function& cb) { +void RPC::getZUnspent(const std::function &cb) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_listunspent"}, - {"params", QJsonArray {0}} // Get UTXOs with 0 confirmations as well. + {"params", QJsonArray{0}} // Get UTXOs with 0 confirmations as well. }; conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::newZaddr(const std::function& cb) { +void RPC::newZaddr(const std::function &cb) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_getnewaddress"}, - {"params", QJsonArray { "sapling" }}, + {"params", QJsonArray{"sapling"}}, }; - + conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::newTaddr(const std::function& cb) { +void RPC::newTaddr(const std::function &cb) +{ QString method = "getnewaddress"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::setGenerate(int proclimit, const std::function& 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}} - }; + {"id", "42"}, + {"method", method}, + {"params", QJsonArray{true, proclimit}}}; conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::stopGenerate(int proclimit, const std::function& 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}} - }; + {"id", "42"}, + {"method", method}, + {"params", QJsonArray{false, proclimit}}}; conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::getZViewKey(QString addr, const std::function& cb) { +void RPC::getZViewKey(QString addr, const std::function &cb) +{ QString method = "z_exportviewingkey"; conn->doRPCWithDefaultErrorHandling(makePayload(method, addr), cb); } -void RPC::getZPrivKey(QString addr, const std::function& cb) { +void RPC::getZPrivKey(QString addr, const std::function &cb) +{ QString method = "z_exportkey"; conn->doRPCWithDefaultErrorHandling(makePayload(method, addr), cb); } -void RPC::getTPrivKey(QString addr, const std::function& cb) { +void RPC::getTPrivKey(QString addr, const std::function &cb) +{ QString method = "dumpprivkey"; conn->doRPCWithDefaultErrorHandling(makePayload(method, addr), cb); } -void RPC::importZPrivKey(QString privkey, bool rescan, const std::function& cb) { +void RPC::importZPrivKey(QString privkey, bool rescan, const std::function &cb) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_importkey"}, - {"params", QJsonArray { privkey, (rescan ? "yes" : "no") }}, + {"params", QJsonArray{privkey, (rescan ? "yes" : "no")}}, }; - + conn->doRPCWithDefaultErrorHandling(payload, cb); } - // TODO: support rescan height and prefix -void RPC::importTPrivKey(QString privkey, bool rescan, const std::function& cb) { +void RPC::importTPrivKey(QString privkey, bool rescan, const std::function &cb) +{ QJsonObject payload; // If privkey starts with 5, K or L, use old-style Hush params, same as BTC+ZEC - if( privkey.startsWith("5") || privkey.startsWith("K") || privkey.startsWith("L") ) { + if (privkey.startsWith("5") || privkey.startsWith("K") || privkey.startsWith("L")) + { qDebug() << "Detected old-style HUSH WIF"; payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "importprivkey"}, - {"params", QJsonArray { privkey, "", "false", "0", "128" }}, + {"params", QJsonArray{privkey, "", "false", "0", "128"}}, }; - } else { + } + else + { qDebug() << "Detected new-style HUSH WIF"; payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "importprivkey"}, - {"params", QJsonArray { privkey, (rescan? "yes" : "no") }}, + {"params", QJsonArray{privkey, (rescan ? "yes" : "no")}}, }; } - qDebug() << "Importing WIF with rescan=" << rescan; + qDebug() << "Importing WIF with rescan=" << rescan; conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::validateAddress(QString address, const std::function& cb) { +void RPC::validateAddress(QString address, const std::function &cb) +{ QString method = address.startsWith("z") ? "z_validateaddress" : "validateaddress"; conn->doRPCWithDefaultErrorHandling(makePayload(method, address), cb); } -void RPC::getBalance(const std::function& cb) { +void RPC::getBalance(const std::function &cb) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_gettotalbalance"}, - {"params", QJsonArray {0}} // Get Unconfirmed balance as well. + {"params", QJsonArray{0}} // Get Unconfirmed balance as well. }; conn->doRPCWithDefaultErrorHandling(payload, cb); } -void RPC::getPeerInfo(const std::function& cb) { +void RPC::getPeerInfo(const std::function &cb) +{ QString method = "getpeerinfo"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::listBanned(const std::function& cb) { +void RPC::listBanned(const std::function &cb) +{ QString method = "listbanned"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::getTransactions(const std::function& cb) { +void RPC::getTransactions(const std::function &cb) +{ QString method = "listtransactions"; conn->doRPCWithDefaultErrorHandling(makePayload(method), cb); } -void RPC::mergeToAddress(QJsonArray ¶ms, const std::function& cb, - const std::function& err) { +void RPC::mergeToAddress(QJsonArray ¶ms, const std::function &cb, + const std::function &err) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_mergetoaddress"}, - {"params", params} - }; + {"params", params}}; - conn->doRPC(payload, cb, [=] (QNetworkReply *reply, const QJsonValue &parsed) { + conn->doRPC(payload, cb, [=](QNetworkReply *reply, const QJsonValue &parsed) + { if (!parsed.isUndefined() && !parsed["error"].toObject()["message"].isNull()) { err(parsed["error"].toObject()["message"].toString()); } else { err(reply->errorString()); - } - }); + } }); } -void RPC::shieldCoinbase(QJsonArray ¶ms, const std::function& cb, - const std::function& err) { +void RPC::shieldCoinbase(QJsonArray ¶ms, const std::function &cb, + const std::function &err) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_shieldcoinbase"}, - {"params", params} - }; + {"params", params}}; - conn->doRPC(payload, cb, [=] (QNetworkReply *reply, const QJsonValue &parsed) { + conn->doRPC(payload, cb, [=](QNetworkReply *reply, const QJsonValue &parsed) + { if (!parsed.isUndefined() && !parsed["error"].toObject()["message"].isNull()) { err(parsed["error"].toObject()["message"].toString()); } else { err(reply->errorString()); - } - }); + } }); } -void RPC::sendZTransaction(QJsonValue params, const std::function& cb, - const std::function& err) { +void RPC::sendZTransaction(QJsonValue params, const std::function &cb, + const std::function &err) +{ QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "z_sendmany"}, - {"params", params} - }; + {"params", params}}; - conn->doRPC(payload, cb, [=] (QNetworkReply *reply, const QJsonValue &parsed) { + conn->doRPC(payload, cb, [=](QNetworkReply *reply, const QJsonValue &parsed) + { if (!parsed.isUndefined() && !parsed["error"].toObject()["message"].isNull()) { err(parsed["error"].toObject()["message"].toString()); } else { err(reply->errorString()); - } - }); + } }); } /** * Method to get all the private keys for both z and t addresses. It will make 2 batch calls, * combine the result, and call the callback with a single list containing both the t-addr and z-addr * private keys - */ -void RPC::getAllPrivKeys(const std::function>)> cb) { - if (conn == nullptr) { + */ +void RPC::getAllPrivKeys(const std::function>)> cb) +{ + if (conn == nullptr) + { // No connection, just return return; } // A special function that will call the callback when two lists have been added auto holder = new QPair>>(); - holder->first = 0; // This is the number of times the callback has been called, initialized to 0 - auto fnCombineTwoLists = [=] (QList> list) { + holder->first = 0; // This is the number of times the callback has been called, initialized to 0 + auto fnCombineTwoLists = [=](QList> list) + { // Increment the callback counter - holder->first++; + holder->first++; // Add all std::copy(list.begin(), list.end(), std::back_inserter(holder->second)); - - // And if the caller has been called twice, do the parent callback with the + + // And if the caller has been called twice, do the parent callback with the // collected list - if (holder->first == 2) { + if (holder->first == 2) + { // Sort so z addresses are on top - std::sort(holder->second.begin(), holder->second.end(), - [=] (auto a, auto b) { return a.first > b.first; }); + std::sort(holder->second.begin(), holder->second.end(), + [=](auto a, auto b) + { return a.first > b.first; }); cb(holder->second); delete holder; - } + } }; // A utility fn to do the batch calling - auto fnDoBatchGetPrivKeys = [=](QJsonValue getAddressPayload, QString privKeyDumpMethodName) { - conn->doRPCWithDefaultErrorHandling(getAddressPayload, [=] (QJsonValue resp) { + auto fnDoBatchGetPrivKeys = [=](QJsonValue getAddressPayload, QString privKeyDumpMethodName) + { + conn->doRPCWithDefaultErrorHandling(getAddressPayload, [=](QJsonValue resp) + { QList addrs; for (auto addr : resp.toArray()) { addrs.push_back(addr.toString()); @@ -518,8 +563,7 @@ void RPC::getAllPrivKeys(const std::function> fnCombineTwoLists(allTKeys); delete privkeys; } - ); - }); + ); }); }; // First get all the t and z addresses. @@ -527,58 +571,57 @@ void RPC::getAllPrivKeys(const std::function> {"jsonrpc", "1.0"}, {"id", "42"}, {"method", "getaddressesbyaccount"}, - {"params", QJsonArray {""} } - }; + {"params", QJsonArray{""}}}; QJsonObject payloadZ = { {"jsonrpc", "1.0"}, {"id", "42"}, - {"method", "z_listaddresses"} - }; + {"method", "z_listaddresses"}}; fnDoBatchGetPrivKeys(payloadT, "dumpprivkey"); fnDoBatchGetPrivKeys(payloadZ, "z_exportkey"); } - // Build the RPC JSON Parameters for this tx -void RPC::fillTxJsonParams(QJsonArray& params, Tx tx) { +void RPC::fillTxJsonParams(QJsonArray ¶ms, Tx tx) +{ Q_ASSERT(QJsonValue(params).isArray()); // Get all the addresses and amounts QJsonArray allRecepients; - // For each addr/amt/memo, construct the JSON and also build the confirm dialog box - for (int i=0; i < tx.toAddrs.size(); i++) { + // For each addr/amt/memo, construct the JSON and also build the confirm dialog box + for (int i = 0; i < tx.toAddrs.size(); i++) + { auto toAddr = tx.toAddrs[i]; // Construct the JSON params QJsonObject rec; - rec["address"] = toAddr.addr; + rec["address"] = toAddr.addr; // Force it through string for rounding. Without this, decimal points beyond 8 places // will appear, causing an "invalid amount" error - rec["amount"] = Settings::getDecimalString(toAddr.amount); //.toDouble(); + rec["amount"] = Settings::getDecimalString(toAddr.amount); //.toDouble(); if (toAddr.addr.startsWith("z") && !toAddr.encodedMemo.trimmed().isEmpty()) - rec["memo"] = toAddr.encodedMemo; + rec["memo"] = toAddr.encodedMemo; allRecepients.push_back(rec); } - // Add sender + // Add sender params.push_back(tx.fromAddr); params.push_back(allRecepients); // Add fees if custom fees are allowed. - if (Settings::getInstance()->getAllowCustomFees()) { + if (Settings::getInstance()->getAllowCustomFees()) + { params.push_back(1); // minconf params.push_back(tx.fee); } - } - -void RPC::noConnection() { +void RPC::noConnection() +{ QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); main->statusIcon->setPixmap(i.pixmap(16, 16)); main->statusIcon->setToolTip(""); @@ -588,7 +631,7 @@ void RPC::noConnection() { // Clear balances table. QMap emptyBalances; - QList emptyOutputs; + QList emptyOutputs; balancesTableModel->setNewData(&emptyBalances, &emptyOutputs); // Clear Transactions table. @@ -612,102 +655,118 @@ void RPC::noConnection() { } // Refresh received z txs by calling z_listreceivedbyaddress/gettransaction -void RPC::refreshReceivedZTrans(QList zaddrs) { - if (conn == nullptr) +void RPC::refreshReceivedZTrans(QList zaddrs) +{ + if (conn == nullptr) return noConnection(); // We'll only refresh the received Z txs if settings allows us. - if (!Settings::getInstance()->getSaveZtxs()) { + if (!Settings::getInstance()->getSaveZtxs()) + { QList emptylist; transactionsTableModel->addZRecvData(emptylist); return; } - - // This method is complicated because z_listreceivedbyaddress only returns the txid, and - // we have to make a follow up call to gettransaction to get details of that transaction. - // Additionally, it has to be done in batches, because there are multiple z-Addresses, - // and each z-Addr can have multiple received txs. - - // 1. For each z-Addr, get list of received txs - conn->doBatchRPC(zaddrs, - [=] (QString zaddr) { + + // This method is complicated because z_listreceivedbyaddress only returns the txid, and + // we have to make a follow up call to gettransaction to get details of that transaction. + // Additionally, it has to be done in batches, because there are multiple z-Addresses, + // and each z-Addr can have multiple received txs. + + // 1. For each z-Addr, get list of received txs + conn->doBatchRPC( + zaddrs, + [=](QString zaddr) + { QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "z_lrba"}, {"method", "z_listreceivedbyaddress"}, - {"params", QJsonArray {zaddr, 0}} // Accept 0 conf as well. + {"params", QJsonArray{zaddr, 0}} // Accept 0 conf as well. }; return payload; - }, - [=] (QMap* zaddrTxids) { + }, + [=](QMap *zaddrTxids) + { // Process all txids, removing duplicates. This can happen if the same address // appears multiple times in a single tx's outputs. QSet txids; QMap memos; - for (auto it = zaddrTxids->constBegin(); it != zaddrTxids->constEnd(); it++) { + for (auto it = zaddrTxids->constBegin(); it != zaddrTxids->constEnd(); it++) + { auto zaddr = it.key(); - for (const auto& i : it.value().toArray()) { + for (const auto &i : it.value().toArray()) + { // Mark the address as used usedAddresses->insert(zaddr, true); // Filter out change txs - if (! i.toObject()["change"].toBool()) { + if (!i.toObject()["change"].toBool()) + { auto txid = i.toObject()["txid"].toString(); - txids.insert(txid); + txids.insert(txid); // Check for Memos QString memoBytes = i.toObject()["memo"].toString(); - if (!memoBytes.startsWith("f600")) { + if (!memoBytes.startsWith("f600")) + { QString memo(QByteArray::fromHex( - i.toObject()["memo"].toString().toUtf8())); + i.toObject()["memo"].toString().toUtf8())); if (!memo.trimmed().isEmpty()) memos[zaddr + txid] = memo; } } - } + } } // 2. For all txids, go and get the details of that txid. - conn->doBatchRPC(txids.toList(), - [=] (QString txid) { + conn->doBatchRPC( + txids.toList(), + [=](QString txid) + { QJsonObject payload = { {"jsonrpc", "1.0"}, - {"id", "gettx"}, + {"id", "gettx"}, {"method", "gettransaction"}, - {"params", QJsonArray {txid}} - }; + {"params", QJsonArray{txid}}}; return payload; }, - [=] (QMap* txidDetails) { + [=](QMap *txidDetails) + { QList txdata; // Combine them both together. For every zAddr's txid, get the amount, fee, confirmations and time - for (auto it = zaddrTxids->constBegin(); it != zaddrTxids->constEnd(); it++) { - for (const auto& i : it.value().toArray()) { + for (auto it = zaddrTxids->constBegin(); it != zaddrTxids->constEnd(); it++) + { + for (const auto &i : it.value().toArray()) + { // Filter out change txs if (i.toObject()["change"].toBool()) continue; - + auto zaddr = it.key(); - auto txid = i.toObject()["txid"].toString(); + auto txid = i.toObject()["txid"].toString(); // Lookup txid in the map auto txidInfo = txidDetails->value(txid); qint64 timestamp; - if (!txidInfo.toObject()["time"].isUndefined()) { + if (!txidInfo.toObject()["time"].isUndefined()) + { timestamp = txidInfo.toObject()["time"].toInt(); - } else { + } + else + { timestamp = txidInfo.toObject()["blocktime"].toInt(); } - - auto amount = i.toObject()["amount"].toDouble(); + + auto amount = i.toObject()["amount"].toDouble(); auto confirmations = (unsigned long)txidInfo["confirmations"].toInt(); - TransactionItem tx{ QString("receive"), timestamp, zaddr, txid, amount, - confirmations, "", memos.value(zaddr + txid, "") }; + TransactionItem tx{QString("receive"), timestamp, zaddr, txid, amount, + confirmations, "", memos.value(zaddr + txid, "")}; txdata.push_front(tx); } } @@ -717,266 +776,301 @@ void RPC::refreshReceivedZTrans(QList zaddrs) { // Cleanup both responses; delete zaddrTxids; delete txidDetails; - } - ); - } - ); -} + }); + }); +} /// This will refresh all the balance data from hushd -void RPC::refresh(bool force) { - if (conn == nullptr) +void RPC::refresh(bool force) +{ + if (conn == nullptr) return noConnection(); getInfoThenRefresh(force); } -void RPC::getInfoThenRefresh(bool force) { - //qDebug() << "getinfo"; - if (conn == nullptr) +void RPC::getInfoThenRefresh(bool force) +{ + // qDebug() << "getinfo"; + if (conn == nullptr) return noConnection(); static bool prevCallSucceeded = false; QString method = "getinfo"; - conn->doRPC(makePayload(method), [=] (const QJsonValue& reply) { - prevCallSucceeded = true; - // Testnet? - if (!reply["testnet"].isNull()) { - Settings::getInstance()->setTestnet(reply["testnet"].toBool()); - }; - - // TODO: checkmark only when getinfo.synced == true! - // Connected, so display checkmark. - QIcon i(":/icons/connected.gif"); - main->statusIcon->setPixmap(i.pixmap(16, 16)); - - static int lastBlock = 0; - int curBlock = reply["blocks"].toInt(); - int longestchain = reply["longestchain"].toInt(); - int version = reply["version"].toInt(); - int p2pport = reply["p2pport"].toInt(); - int rpcport = reply["rpcport"].toInt(); - int notarized = reply["notarized"].toInt(); - int protocolversion = reply["protocolversion"].toInt(); - int lag = curBlock - notarized; - // TODO: store all future halvings - int blocks_until_halving= 2020000 - curBlock; - char halving_days[8]; - int blocktime = 75; // seconds - if(isdragonx) { - blocks_until_halving= 3500000 - curBlock; - blocktime = 60; - } - sprintf(halving_days, "%.2f", (double) (blocks_until_halving * blocktime) / (60*60*24) ); - QString ntzhash = reply["notarizedhash"].toString(); - QString ntztxid = reply["notarizedtxid"].toString(); - - Settings::getInstance()->setHushdVersion(version); - - ui->longestchain->setText(QString::number(longestchain)); - // ui->notarizedhashvalue->setText( ntzhash ); - // ui->notarizedtxidvalue->setText( ntztxid ); - // ui->lagvalue->setText( QString::number(lag) ); - ui->version->setText( QString::number(version) ); - ui->protocolversion->setText( QString::number(protocolversion) ); - ui->p2pport->setText( QString::number(p2pport) ); - ui->rpcport->setText( QString::number(rpcport) ); - ui->halving->setText( QString::number(blocks_until_halving) % " blocks, " % QString::fromStdString(halving_days) % " days" ); - - if ( force || (curBlock != lastBlock) ) { - // Something changed, so refresh everything. - lastBlock = curBlock; - - refreshBalances(); - refreshPeers(); - refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans() - refreshTransactions(); - } - - int connections = reply["connections"].toInt(); - bool hasTLS = !reply["tls_connections"].isNull(); - qDebug() << "Local node TLS support = " << hasTLS; - int tlsconnections = hasTLS ? reply["tls_connections"].toInt() : 0; - Settings::getInstance()->setPeers(connections); + conn->doRPC( + makePayload(method), [=](const QJsonValue &reply) + { + prevCallSucceeded = true; + // Testnet? + if (!reply["testnet"].isNull()) + { + Settings::getInstance()->setTestnet(reply["testnet"].toBool()); + }; - if (connections == 0) { - // If there are no peers connected, then the internet is probably off or something else is wrong. - QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + // TODO: checkmark only when getinfo.synced == true! + // Connected, so display checkmark. + QIcon i(":/icons/connected.gif"); main->statusIcon->setPixmap(i.pixmap(16, 16)); - } - - 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) { - qint64 solrate = reply.toInt(); - //TODO: format decimal - if(isdragonx) { - ui->solrate->setText(QString::number((double)solrate) % " Hash/s"); - } else { - ui->solrate->setText(QString::number((double)solrate / 1000000) % " MegaSol/s"); + static int lastBlock = 0; + int curBlock = reply["blocks"].toInt(); + int longestchain = reply["longestchain"].toInt(); + int version = reply["version"].toInt(); + int p2pport = reply["p2pport"].toInt(); + int rpcport = reply["rpcport"].toInt(); + int notarized = reply["notarized"].toInt(); + int protocolversion = reply["protocolversion"].toInt(); + int lag = curBlock - notarized; + // TODO: store all future halvings + int blocks_until_halving = 2020000 - curBlock; + char halving_days[8]; + int blocktime = 75; // seconds + if (isHSC) + { + blocks_until_halving = 3500000 - curBlock; + blocktime = 60; + } + sprintf(halving_days, "%.2f", (double)(blocks_until_halving * blocktime) / (60 * 60 * 24)); + QString ntzhash = reply["notarizedhash"].toString(); + QString ntztxid = reply["notarizedtxid"].toString(); + + Settings::getInstance()->setHushdVersion(version); + + ui->longestchain->setText(QString::number(longestchain)); + // ui->notarizedhashvalue->setText( ntzhash ); + // ui->notarizedtxidvalue->setText( ntztxid ); + // ui->lagvalue->setText( QString::number(lag) ); + ui->version->setText(QString::number(version)); + ui->protocolversion->setText(QString::number(protocolversion)); + ui->p2pport->setText(QString::number(p2pport)); + ui->rpcport->setText(QString::number(rpcport)); + ui->halving->setText(QString::number(blocks_until_halving) % " blocks, " % QString::fromStdString(halving_days) % " days"); + + if (force || (curBlock != lastBlock)) + { + // Something changed, so refresh everything. + lastBlock = curBlock; + + refreshBalances(); + refreshPeers(); + refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans() + refreshTransactions(); } - // 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"; + int connections = reply["connections"].toInt(); + bool hasTLS = !reply["tls_connections"].isNull(); + qDebug() << "Local node TLS support = " << hasTLS; + int tlsconnections = hasTLS ? reply["tls_connections"].toInt() : 0; + Settings::getInstance()->setPeers(connections); + + if (connections == 0) + { + // If there are no peers connected, then the internet is probably off or something else is wrong. + QIcon i = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + main->statusIcon->setPixmap(i.pixmap(16, 16)); } - // hashrate->display( QString::number( (double) solrate) ); - }); - // only look at local hashrate for dragonx - if(isdragonx) { - QString method = "getlocalsolps"; - DEBUG("calling getlocalsolps"); + 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) { - 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"); + qint64 solrate = reply.toInt(); + + //TODO: format decimal + if(isHSC) { + ui->solrate->setText(QString::number((double)solrate) % " Hash/s"); + } 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( (double) solrate, 'f', 2 ) ); + hashrate->display( QString::number( solrate, 'f', 2 ) ); } else { - qDebug() << "no widget named localhashrate found"; + qDebug() << "no widget named networkhashrate found"; } + // hashrate->display( QString::number( (double) solrate) ); }); - } - */ - - // 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() ); - - // 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"); - // The rest of this callback is for DRAGONX - return; - } - - 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 (stopbutton == nullptr) { - return; - } - if (startbutton == nullptr) { - return; - } - - 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"); - } - - // 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 - //TODO: maybe use this as a tooltip - double percentOfNetHash = localhashps.toDouble() / networkhashps.toDouble(); - DEBUG( "% of nethash=" << percentOfNetHash ); - - if (localhashps.toDouble() > 0) { - //TODO: this is only for DRAGONX - int blocktime = 36; - double luckInSeconds = (networkhashps.toDouble()/localhashps.toDouble())*blocktime; - double luckInHours = luckInSeconds / (60*60); - luck->display( QString::number( luckInHours , 'f', 2 ) ); + // only look at local hashrate for dragonx + if(isHSC) { + 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"; } - } - } else { - qDebug() << "no widget named luck found"; + }); } - - }); - - // Get network info - conn->doRPCIgnoreError(makePayload("getnetworkinfo"), [=](const QJsonValue& reply) { + */ + + // 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()); + + // Update network hashrate in "Node Info" tab + if (isHSC) + { + ui->solrate->setText(QString::number(networkhashps.toDouble()) % " Hash/s"); + } + else + { + ui->solrate->setText(QString::number(networkhashps.toDouble() / 1000000) % " MegaSol/s"); + // The rest of this callback is for DRAGONX + return; + } + + 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 (stopbutton == nullptr) + { + return; + } + if (startbutton == nullptr) + { + return; + } + + 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"); + } + + // 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 + // TODO: maybe use this as a tooltip + double percentOfNetHash = localhashps.toDouble() / networkhashps.toDouble(); + DEBUG("% of nethash=" << percentOfNetHash); + + if (localhashps.toDouble() > 0) + { + // TODO: this is only for DRAGONX + int blocktime = 36; + double luckInSeconds = (networkhashps.toDouble() / localhashps.toDouble()) * blocktime; + double luckInHours = luckInSeconds / (60 * 60); + luck->display(QString::number(luckInHours, 'f', 2)); + } + } + } + else + { + qDebug() << "no widget named luck found"; + } + }); + + // Get network info + conn->doRPCIgnoreError(makePayload("getnetworkinfo"), [=](const QJsonValue &reply) + { QString clientname = reply["subversion"].toString(); QString localservices = reply["localservices"].toString(); ui->clientname->setText(clientname); - ui->localservices->setText(localservices); - }); + ui->localservices->setText(localservices); }); - conn->doRPCIgnoreError(makePayload("getwalletinfo"), [=](const QJsonValue& reply) { + conn->doRPCIgnoreError(makePayload("getwalletinfo"), [=](const QJsonValue &reply) + { int txcount = reply["txcount"].toInt(); - ui->txcount->setText(QString::number(txcount)); - }); + ui->txcount->setText(QString::number(txcount)); }); - //TODO: If -zindex is enabled, show stats - conn->doRPCIgnoreError(makePayload("getchaintxstats"), [=](const QJsonValue& reply) { + // TODO: If -zindex is enabled, show stats + conn->doRPCIgnoreError(makePayload("getchaintxstats"), [=](const QJsonValue &reply) + { int txcount = reply["txcount"].toInt(); - ui->chaintxcount->setText(QString::number(txcount)); - }); + ui->chaintxcount->setText(QString::number(txcount)); }); - // Call to see if the blockchain is syncing. - conn->doRPCIgnoreError(makePayload("getblockchaininfo"), [=](const QJsonValue& reply) { + // Call to see if the blockchain is syncing. + conn->doRPCIgnoreError(makePayload("getblockchaininfo"), [=](const QJsonValue &reply) + { auto progress = reply["verificationprogress"].toDouble(); // TODO: use getinfo.synced bool isSyncing = progress < 0.9999; // 99.99% @@ -1013,7 +1107,7 @@ void RPC::getInfoThenRefresh(bool force) { QString price = ""; // No price data for dragonx for now - if (!isdragonx) { + if (!isHSC) { auto ticker_price = s->get_price(ticker); if(ticker_price > 0 && ticker != "BTC") { extra = QString::number( s->getBTCPrice() ) % "sat"; @@ -1043,37 +1137,38 @@ void RPC::getInfoThenRefresh(bool force) { } tooltip = tooltip % "(v" % QString::number(Settings::getInstance()->getHushdVersion()) % ")"; - if (!isdragonx && !hushPrice.isEmpty()) { + if (!isHSC && !hushPrice.isEmpty()) { tooltip = "1 HUSH = " % hushPrice % "\n" % tooltip; } main->statusLabel->setToolTip(tooltip); - main->statusIcon->setToolTip(tooltip); - }); - - }, [=](QNetworkReply* reply, const QJsonValue&) { - // hushd has probably disappeared. - this->noConnection(); - - // Prevent multiple dialog boxes, because these are called async - static bool shown = false; - if (!shown && prevCallSucceeded) { // show error only first time - shown = true; - QMessageBox::critical(main, QObject::tr("Connection Error"), QObject::tr("There was an error connecting to hushd. The error was") + ": \n\n" - + reply->errorString(), QMessageBox::StandardButton::Ok); - shown = false; - } + main->statusIcon->setToolTip(tooltip); }); }, + [=](QNetworkReply *reply, const QJsonValue &) + { + // hushd has probably disappeared. + this->noConnection(); + + // Prevent multiple dialog boxes, because these are called async + static bool shown = false; + if (!shown && prevCallSucceeded) + { // show error only first time + shown = true; + QMessageBox::critical(main, QObject::tr("Connection Error"), QObject::tr("There was an error connecting to hushd. The error was") + ": \n\n" + reply->errorString(), QMessageBox::StandardButton::Ok); + shown = false; + } - prevCallSucceeded = false; - }); + prevCallSucceeded = false; + }); } -void RPC::refreshAddresses() { - if (conn == nullptr) +void RPC::refreshAddresses() +{ + if (conn == nullptr) return noConnection(); - + auto newzaddresses = new QList(); - getZAddresses([=] (QJsonValue reply) { + getZAddresses([=](QJsonValue reply) + { for (const auto& it : reply.toArray()) { auto addr = it.toString(); newzaddresses->push_back(addr); @@ -1084,12 +1179,11 @@ void RPC::refreshAddresses() { // Refresh the sent and received txs from all these z-addresses refreshSentZTrans(); - refreshReceivedZTrans(*zaddresses); - }); + refreshReceivedZTrans(*zaddresses); }); - auto newtaddresses = new QList(); - getTAddresses([=] (QJsonValue reply) { + getTAddresses([=](QJsonValue reply) + { for (const auto& it : reply.toArray()) { auto addr = it.toString(); if (Settings::isTAddress(addr)) @@ -1097,12 +1191,12 @@ void RPC::refreshAddresses() { } delete taddresses; - taddresses = newtaddresses; - }); + taddresses = newtaddresses; }); } // Function to create the data model and update the views, used below. -void RPC::updateUI(bool anyUnconfirmed) { +void RPC::updateUI(bool anyUnconfirmed) +{ ui->unconfirmedWarning->setVisible(anyUnconfirmed); // Update balances model data, which will update the table too @@ -1113,31 +1207,36 @@ void RPC::updateUI(bool anyUnconfirmed) { }; // Function to process reply of the listunspent and z_listunspent API calls, used below. -bool RPC::processUnspent(const QJsonValue& reply, QMap* balancesMap, QList* newUtxos) { +bool RPC::processUnspent(const QJsonValue &reply, QMap *balancesMap, QList *newUtxos) +{ bool anyUnconfirmed = false; - for (const auto& it : reply.toArray()) { + for (const auto &it : reply.toArray()) + { QString qsAddr = it.toObject()["address"].toString(); auto confirmations = it.toObject()["confirmations"].toInt(); - if (confirmations == 0) { + if (confirmations == 0) + { anyUnconfirmed = true; } newUtxos->push_back( - UnspentOutput{ qsAddr, it.toObject()["txid"].toString(), - Settings::getDecimalString(it.toObject()["amount"].toDouble()), - (int)confirmations, it.toObject()["spendable"].toBool() }); + UnspentOutput{qsAddr, it.toObject()["txid"].toString(), + Settings::getDecimalString(it.toObject()["amount"].toDouble()), + (int)confirmations, it.toObject()["spendable"].toBool()}); (*balancesMap)[qsAddr] = (*balancesMap)[qsAddr] + it.toObject()["amount"].toDouble(); } return anyUnconfirmed; }; -void RPC::refreshBalances() { - if (conn == nullptr) +void RPC::refreshBalances() +{ + if (conn == nullptr) return noConnection(); // 1. Get the Balances - getBalance([=] (QJsonValue reply) { + getBalance([=](QJsonValue reply) + { auto balT = reply["transparent"].toString().toDouble(); auto balZ = reply["private"].toString().toDouble(); @@ -1152,8 +1251,7 @@ void RPC::refreshBalances() { ui->balTotal ->setToolTip(Settings::getUSDFormat(balTotal)); ui->balUSDTotal ->setText(Settings::getUSDFormat(balTotal)); - ui->balUSDTotal ->setToolTip(Settings::getUSDFormat(balTotal)); - }); + ui->balUSDTotal ->setToolTip(Settings::getUSDFormat(balTotal)); }); // 2. Get the UTXOs // First, create a new UTXO list. It will be replacing the existing list when everything is processed. @@ -1161,7 +1259,8 @@ void RPC::refreshBalances() { auto newBalances = new QMap(); // Call the Transparent and Z unspent APIs serially and then, once they're done, update the UI - getTransparentUnspent([=] (QJsonValue reply) { + getTransparentUnspent([=](QJsonValue reply) + { auto anyTUnconfirmed = processUnspent(reply, newBalances, newUtxos); getZUnspent([=] (QJsonValue reply) { @@ -1177,18 +1276,20 @@ void RPC::refreshBalances() { updateUI(anyTUnconfirmed || anyZUnconfirmed); main->balancesReady(); - }); - }); + }); }); } -void RPC::refreshRescan() { +void RPC::refreshRescan() +{ qDebug() << __func__; - if (conn == nullptr) { + if (conn == nullptr) + { qDebug() << __func__ << ": no connection"; return noConnection(); } - getRescanInfo([=] (QJsonValue response){ + getRescanInfo([=](QJsonValue response) + { qDebug() << "got getrescaninfo json=" << response; auto rescanning = response.toObject().value("rescanning").toBool(); auto rescan_progress = response.toObject().value("rescan_progress").toString(); @@ -1203,29 +1304,30 @@ void RPC::refreshRescan() { ui->statusBar->showMessage(QObject::tr("Rescanning... ") + QString::number(percent)+ "% " + QObject::tr("at height") + " " + QString::number(rescan_height)); } else{ qDebug() << __func__ << ": not rescanning"; - } - }); + } }); } -void RPC::refreshPeers() { +void RPC::refreshPeers() +{ qDebug() << __func__; - if (conn == nullptr) + if (conn == nullptr) return noConnection(); -/* -[ - { - "address": "199.247.28.148/255.255.255.255", - "banned_until": 1612869516 - }, - { - "address": "2001:19f0:5001:d26:5400:3ff:fe18:f6c2/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - "banned_until": 1612870431 - } -] -*/ - - listBanned([=] (QJsonValue reply) { + /* + [ + { + "address": "199.247.28.148/255.255.255.255", + "banned_until": 1612869516 + }, + { + "address": "2001:19f0:5001:d26:5400:3ff:fe18:f6c2/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "banned_until": 1612870431 + } + ] + */ + + listBanned([=](QJsonValue reply) + { QList peerdata; for (const auto& it : reply.toArray()) { auto addr = it.toObject()["address"].toString(); @@ -1238,10 +1340,10 @@ void RPC::refreshPeers() { qDebug() << "Adding banned peer with address=" << addr; peerdata.push_back(peer); } - bannedPeersTableModel->addData(peerdata); - }); + bannedPeersTableModel->addData(peerdata); }); - getPeerInfo([=] (QJsonValue reply) { + getPeerInfo([=](QJsonValue reply) + { QList peerdata; for (const auto& it : reply.toArray()) { @@ -1274,18 +1376,19 @@ void RPC::refreshPeers() { //qDebug() << peerdata; // Update model data, which updates the table view - peersTableModel->addData(peerdata); - }); + peersTableModel->addData(peerdata); }); } -void RPC::refreshTransactions() { - if (conn == nullptr) +void RPC::refreshTransactions() +{ + if (conn == nullptr) return noConnection(); // Show statusBar message ui->statusBar->showMessage(QObject::tr("Transaction data is loading...")); - getTransactions([=] (QJsonValue reply) { + getTransactions([=](QJsonValue reply) + { QList txdata; for (const auto& it : reply.toArray()) { @@ -1315,49 +1418,54 @@ void RPC::refreshTransactions() { transactionsTableModel->addTData(txdata); // Update statusBar message - ui->statusBar->showMessage(QObject::tr("Transaction data loaded"), 3 * 1000); - }); + ui->statusBar->showMessage(QObject::tr("Transaction data loaded"), 3 * 1000); }); } // Read sent Z transactions from the file. -void RPC::refreshSentZTrans() { - if (conn == nullptr) +void RPC::refreshSentZTrans() +{ + if (conn == nullptr) return noConnection(); auto sentZTxs = SentTxStore::readSentTxFile(); - // If there are no sent z txs, then empty the table. + // If there are no sent z txs, then empty the table. // This happens when you clear history. - if (sentZTxs.isEmpty()) { + if (sentZTxs.isEmpty()) + { transactionsTableModel->addZSentData(sentZTxs); return; } QList txids; - for (auto sentTx: sentZTxs) { + for (auto sentTx : sentZTxs) + { txids.push_back(sentTx.txid); } - // Look up all the txids to get the confirmation count for them. - conn->doBatchRPC(txids, - [=] (QString txid) { + // Look up all the txids to get the confirmation count for them. + conn->doBatchRPC( + txids, + [=](QString txid) + { QJsonObject payload = { {"jsonrpc", "1.0"}, {"id", "senttxid"}, {"method", "gettransaction"}, - {"params", QJsonArray {txid}} - }; + {"params", QJsonArray{txid}}}; return payload; - }, - [=] (QMap* txidList) { + }, + [=](QMap *txidList) + { auto newSentZTxs = sentZTxs; // Update the original sent list with the confirmation count // TODO: This whole thing is kinda inefficient. We should probably just update the file // with the confirmed block number, so we don't have to keep calling gettransaction for the // sent items. - for (TransactionItem& sentTx: newSentZTxs) { + for (TransactionItem &sentTx : newSentZTxs) + { auto j = txidList->value(sentTx.txid); if (j.isNull()) continue; @@ -1368,46 +1476,49 @@ void RPC::refreshSentZTrans() { transactionsTableModel->addZSentData(newSentZTxs); delete txidList; - } - ); + }); } -void RPC::addNewTxToWatch(const QString& newOpid, WatchedTx wtx) { +void RPC::addNewTxToWatch(const QString &newOpid, WatchedTx wtx) +{ watchingOps.insert(newOpid, wtx); watchTxStatus(); } - // Execute a transaction! -void RPC::executeTransaction(Tx tx, - const std::function submitted, - const std::function computed, - const std::function error) { +void RPC::executeTransaction(Tx tx, + const std::function submitted, + const std::function computed, + const std::function error) +{ // First, create the json params QJsonArray params; fillTxJsonParams(params, tx); - //std::cout << std::setw(2) << params << std::endl; + // std::cout << std::setw(2) << params << std::endl; - sendZTransaction(params, [=](const QJsonValue& reply) { + sendZTransaction( + params, [=](const QJsonValue &reply) + { QString opid = reply.toString(); // And then start monitoring the transaction addNewTxToWatch( opid, WatchedTx { opid, tx, computed, error} ); - submitted(opid); - }, - [=](QString errStr) { - error("", errStr); - }); + submitted(opid); }, + [=](QString errStr) + { + error("", errStr); + }); } - -void RPC::watchTxStatus() { - if (conn == nullptr) +void RPC::watchTxStatus() +{ + if (conn == nullptr) return noConnection(); // Make an RPC to load pending operation statues - conn->doRPCIgnoreError(makePayload("z_getoperationstatus"), [=] (const QJsonValue& reply) { + conn->doRPCIgnoreError(makePayload("z_getoperationstatus"), [=](const QJsonValue &reply) + { // conn->doRPCIgnoreError(payload, [=] (const json& reply) { // There's an array for each item in the status for (const auto& it : reply.toArray()) { @@ -1458,17 +1569,17 @@ void RPC::watchTxStatus() { } else { main->loadingLabel->setVisible(true); main->loadingLabel->setToolTip(QString::number(watchingOps.size()) + QObject::tr(" transaction computing.")); - } - }); + } }); } -void RPC::checkForUpdate(bool silent) { +void RPC::checkForUpdate(bool silent) +{ // Disable update checks for now // TODO: Use Gitea API return; qDebug() << "checking for updates"; - if (conn == nullptr) + if (conn == nullptr) return noConnection(); QUrl cmcURL("https://git.hush.is/hush/SilentDragon/releases"); @@ -1477,7 +1588,8 @@ void RPC::checkForUpdate(bool silent) { req.setUrl(cmcURL); QNetworkReply *reply = conn->restclient->get(req); - QObject::connect(reply, &QNetworkReply::finished, [=] { + QObject::connect(reply, &QNetworkReply::finished, [=] + { reply->deleteLater(); try { @@ -1531,22 +1643,24 @@ void RPC::checkForUpdate(bool silent) { } catch (const std::exception& e) { // If anything at all goes wrong, move on qDebug() << QString("Exception checking for updates!"); - } - }); + } }); } // Get the HUSH prices -void RPC::refreshPrice() { - if (conn == nullptr) +void RPC::refreshPrice() +{ + if (conn == nullptr) return noConnection(); - if (isdragonx) { + if (isHSC) + { return; } auto s = Settings::getInstance(); - if (s->getAllowFetchPrices() == false) { + if (s->getAllowFetchPrices() == false) + { qDebug() << "Price fetching disabled"; return; } @@ -1563,8 +1677,8 @@ void RPC::refreshPrice() { QNetworkReply *reply = conn->restclient->get(req); qDebug() << "Created QNetworkReply"; - - QObject::connect(reply, &QNetworkReply::finished, [=] { + QObject::connect(reply, &QNetworkReply::finished, [=] + { reply->deleteLater(); try { @@ -1658,13 +1772,14 @@ void RPC::refreshPrice() { // If nothing, then set the price to 0; Settings::getInstance()->setHUSHPrice(0); - Settings::getInstance()->setBTCPrice(0); - }); + Settings::getInstance()->setBTCPrice(0); }); } -void RPC::shutdownHushd() { +void RPC::shutdownHushd() +{ // Shutdown embedded hushd if it was started - if (ehushd == nullptr || ehushd->processId() == 0 || conn == nullptr) { + if (ehushd == nullptr || ehushd->processId() == 0 || conn == nullptr) + { // No hushd running internally, just return return; } @@ -1677,31 +1792,39 @@ void RPC::shutdownHushd() { d.setWindowFlags(d.windowFlags() & ~(Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint)); Ui_ConnectionDialog connD; connD.setupUi(&d); - //connD.topIcon->setBasePixmap(QIcon(":/icons/icon.ico").pixmap(256, 256)); - if(isdragonx) { - d.setWindowTitle("SilentDragonX"); + // connD.topIcon->setBasePixmap(QIcon(":/icons/icon.ico").pixmap(256, 256)); + if (isHSC) + { + d.setWindowTitle(QString::fromStdString(App_title)); } - QMovie *movie1 = new QMovie(":/img/silentdragon-animated-dark.gif");; + QMovie *movie1 = new QMovie(":/img/silentdragon-animated-dark.gif"); + ; auto theme = Settings::getInstance()->get_theme_name(); - movie1->setScaledSize(QSize(512,512)); + movie1->setScaledSize(QSize(512, 512)); connD.topIcon->setMovie(movie1); movie1->start(); - if(isdragonx) { - connD.status->setText(QObject::tr("Please enhance your calm and wait for SilentDragonX to exit")); - connD.statusDetail->setText(QObject::tr("Waiting for dragonxd to exit, y'all")); - } else { + if (isHSC) + { + std::string exit_message = "Please enhance your calm and wait for " + App_title + " to exit"; + std::string exit_detail = "Waiting for " + HSC_name + "d to exit, y'all"; + connD.status->setText((QObject::tr(exit_message.c_str()))); + connD.statusDetail->setText((QObject::tr(exit_detail.c_str()))); + } + else + { connD.status->setText(QObject::tr("Please enhance your calm and wait for SilentDragon to exit")); connD.statusDetail->setText(QObject::tr("Waiting for hushd to exit, y'all")); } QTimer waiter(main); - // We capture by reference all the local variables because of the d.exec() - // below, which blocks this function until we exit. + // We capture by reference all the local variables because of the d.exec() + // below, which blocks this function until we exit. int waitCount = 0; - QObject::connect(&waiter, &QTimer::timeout, [&] () { + QObject::connect(&waiter, &QTimer::timeout, [&]() + { waitCount++; if ((ehushd->atEnd() && ehushd->processId() == 0) || @@ -1713,15 +1836,18 @@ void RPC::shutdownHushd() { QTimer::singleShot(1000, [&]() { d.accept(); }); } else { qDebug() << "Not ended, continuing to wait..."; - } - }); + } }); waiter.start(1000); // Wait for the hush process to exit. - if (!Settings::getInstance()->isHeadless()) { - d.exec(); - } else { - while (waiter.isActive()) { + if (!Settings::getInstance()->isHeadless()) + { + d.exec(); + } + else + { + while (waiter.isActive()) + { QCoreApplication::processEvents(); QThread::sleep(1); @@ -1729,12 +1855,13 @@ void RPC::shutdownHushd() { } } - -/** +/** * Get a Sapling address from the user's wallet - */ -QString RPC::getDefaultSaplingAddress() { - for (QString addr: *zaddresses) { + */ +QString RPC::getDefaultSaplingAddress() +{ + for (QString addr : *zaddresses) + { if (Settings::getInstance()->isSaplingAddress(addr)) return addr; } @@ -1742,9 +1869,10 @@ QString RPC::getDefaultSaplingAddress() { return QString(); } -QString RPC::getDefaultTAddress() { +QString RPC::getDefaultTAddress() +{ if (getAllTAddresses()->length() > 0) return getAllTAddresses()->at(0); - else + else return QString(); } diff --git a/src/senttxstore.cpp b/src/senttxstore.cpp index 407552d..aa72ff4 100644 --- a/src/senttxstore.cpp +++ b/src/senttxstore.cpp @@ -3,54 +3,65 @@ #include "senttxstore.h" #include "settings.h" -extern bool isdragonx; +extern bool isHSC; +extern std::string HSC_ticker; -/// Get the location of the app data file to be written. -QString SentTxStore::writeableFile() { +/// Get the location of the app data file to be written. +QString SentTxStore::writeableFile() +{ auto filename = QStringLiteral("senttxstore.dat"); - if (isdragonx) { - filename = QStringLiteral("senttxstore-drgx.dat"); + if (isHSC) + { + std::string ticker = ("senttxstore-" + HSC_ticker + ".dat"); + filename = QString::fromStdString(ticker); } auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); if (!dir.exists()) QDir().mkpath(dir.absolutePath()); - if (Settings::getInstance()->isTestnet()) { + if (Settings::getInstance()->isTestnet()) + { return dir.filePath("testnet-" % filename); - } else { + } + else + { qDebug() << "senttxstore file = " + dir.filePath(filename); return dir.filePath(filename); } } -// delete the sent history. -void SentTxStore::deleteHistory() { +// delete the sent history. +void SentTxStore::deleteHistory() +{ QFile data(writeableFile()); data.remove(); data.close(); } -QList SentTxStore::readSentTxFile() { +QList SentTxStore::readSentTxFile() +{ QFile data(writeableFile()); - if (!data.exists()) { + if (!data.exists()) + { return QList(); } - + QJsonDocument jsonDoc; - - data.open(QFile::ReadOnly); + + data.open(QFile::ReadOnly); jsonDoc = QJsonDocument::fromJson(data.readAll()); data.close(); QList items; - for (auto i : jsonDoc.array()) { + for (auto i : jsonDoc.array()) + { auto sentTx = i.toObject(); - TransactionItem t{"send", (qint64)sentTx["datetime"].toVariant().toLongLong(), - sentTx["address"].toString(), - sentTx["txid"].toString(), - sentTx["amount"].toDouble() + sentTx["fee"].toDouble(), + TransactionItem t{"send", (qint64)sentTx["datetime"].toVariant().toLongLong(), + sentTx["address"].toString(), + sentTx["txid"].toString(), + sentTx["amount"].toDouble() + sentTx["fee"].toDouble(), 0, sentTx["from"].toString(), sentTx["memo"].toString()}; items.push_back(t); } @@ -58,21 +69,23 @@ QList SentTxStore::readSentTxFile() { return items; } -void SentTxStore::addToSentTx(Tx tx, QString txid) { +void SentTxStore::addToSentTx(Tx tx, QString txid) +{ // Save transactions only if the settings are allowed if (!Settings::getInstance()->getSaveZtxs()) return; - // Also, only store outgoing txs where the from address is a z-Addr. Else, regular hushd + // Also, only store outgoing txs where the from address is a z-Addr. Else, regular hushd // stores it just fine - if (!tx.fromAddr.startsWith("z")) + if (!tx.fromAddr.startsWith("z")) return; QFile data(writeableFile()); QJsonDocument jsonDoc; // If data doesn't exist, then create a blank one - if (!data.exists()) { + if (!data.exists()) + { QJsonArray a; jsonDoc.setArray(a); @@ -80,46 +93,54 @@ void SentTxStore::addToSentTx(Tx tx, QString txid) { newFile.open(QFile::WriteOnly); newFile.write(jsonDoc.toJson()); newFile.close(); - } else { - data.open(QFile::ReadOnly); + } + else + { + data.open(QFile::ReadOnly); jsonDoc = QJsonDocument().fromJson(data.readAll()); data.close(); } // Calculate total amount in this tx double totalAmount = 0; - for (auto i : tx.toAddrs) { + for (auto i : tx.toAddrs) + { totalAmount += i.amount; } QString toAddresses; - if (tx.toAddrs.length() == 1) { + if (tx.toAddrs.length() == 1) + { toAddresses = tx.toAddrs[0].addr; - } else { + } + else + { // Concatenate all the toAddresses - for (auto a : tx.toAddrs) { + for (auto a : tx.toAddrs) + { toAddresses += a.addr % "(" % Settings::getDisplayFormat(a.amount) % ") "; } } auto list = jsonDoc.array(); QJsonObject txItem; - txItem["type"] = "sent"; - txItem["from"] = tx.fromAddr; - txItem["datetime"] = QDateTime::currentMSecsSinceEpoch() / (qint64)1000; - txItem["address"] = toAddresses; - txItem["txid"] = txid; - txItem["amount"] = -totalAmount; - txItem["fee"] = -tx.fee; + txItem["type"] = "sent"; + txItem["from"] = tx.fromAddr; + txItem["datetime"] = QDateTime::currentMSecsSinceEpoch() / (qint64)1000; + txItem["address"] = toAddresses; + txItem["txid"] = txid; + txItem["amount"] = -totalAmount; + txItem["fee"] = -tx.fee; // TODO: store all outgoing memos - txItem["memo"] = tx.toAddrs[0].txtMemo; + txItem["memo"] = tx.toAddrs[0].txtMemo; list.append(txItem); jsonDoc.setArray(list); QFile writer(writeableFile()); - if (writer.open(QFile::WriteOnly | QFile::Truncate)) { + if (writer.open(QFile::WriteOnly | QFile::Truncate)) + { writer.write(jsonDoc.toJson()); - } + } writer.close(); } diff --git a/src/settings.cpp b/src/settings.cpp index 3d8dd11..969b4d5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -4,92 +4,107 @@ #include "settings.h" #include "sd.h" -extern bool isdragonx; +extern bool isHSC; +extern std::string HSC_explorer; +extern std::string HSC_ticker; -Settings* Settings::instance = nullptr; +Settings *Settings::instance = nullptr; -Settings* Settings::init() { +Settings *Settings::init() +{ if (instance == nullptr) instance = new Settings(); return instance; } -Settings* Settings::getInstance() { +Settings *Settings::getInstance() +{ return instance; } -bool Settings::getCheckForUpdates() { +bool Settings::getCheckForUpdates() +{ return QSettings().value("options/allowcheckupdates", true).toBool(); } -void Settings::setCheckForUpdates(bool allow) { - QSettings().setValue("options/allowcheckupdates", allow); +void Settings::setCheckForUpdates(bool allow) +{ + QSettings().setValue("options/allowcheckupdates", allow); } -bool Settings::getAllowFetchPrices() { +bool Settings::getAllowFetchPrices() +{ // now defaults to OFF, used to be ON return QSettings().value("options/allowfetchprices", false).toBool(); } -void Settings::setAllowFetchPrices(bool allow) { - QSettings().setValue("options/allowfetchprices", allow); +void Settings::setAllowFetchPrices(bool allow) +{ + QSettings().setValue("options/allowfetchprices", allow); } -Explorer Settings::getExplorer() { +Explorer Settings::getExplorer() +{ // Load from the QT Settings. QSettings s; - //TODO: make it easy for people to use other explorers + // TODO: make it easy for people to use other explorers QString explorer = "https://explorer.hush.is"; - if(isdragonx) { - explorer = "https://explorer.dragonx.is"; + if (isHSC) + { + explorer = QString::fromStdString(HSC_explorer); } - QString onionExplorer = "http://jlqhwhak65kokg3pdjp3ufy6almf5spnhsfnugtjsc4z7wtapgozxyad.onion"; - auto txExplorerUrl = s.value("explorer/txExplorerUrl", explorer + "/tx/").toString(); - auto addressExplorerUrl = s.value("explorer/addressExplorerUrl", explorer + "/address/").toString(); - auto onionTxExplorerUrl = s.value("explorer/onionTxExplorerUrl", onionExplorer + "/tx/").toString(); + QString onionExplorer = "http://jlqhwhak65kokg3pdjp3ufy6almf5spnhsfnugtjsc4z7wtapgozxyad.onion"; + auto txExplorerUrl = s.value("explorer/txExplorerUrl", explorer + "/tx/").toString(); + auto addressExplorerUrl = s.value("explorer/addressExplorerUrl", explorer + "/address/").toString(); + auto onionTxExplorerUrl = s.value("explorer/onionTxExplorerUrl", onionExplorer + "/tx/").toString(); auto onionAddressExplorerUrl = s.value("explorer/onionAddressExplorerUrl", onionExplorer + "/address/").toString(); // Some users have the old malicious explorer URL saved in their config file, help them out - if (txExplorerUrl == "https://explorer.myhush.org/tx/") { + if (txExplorerUrl == "https://explorer.myhush.org/tx/") + { txExplorerUrl = explorer + "/tx/"; saveExplorer(txExplorerUrl, addressExplorerUrl, onionTxExplorerUrl, onionAddressExplorerUrl); } - if (addressExplorerUrl == "https://explorer.myhush.org/address/") { + if (addressExplorerUrl == "https://explorer.myhush.org/address/") + { addressExplorerUrl = explorer + "/address/"; saveExplorer(txExplorerUrl, addressExplorerUrl, onionTxExplorerUrl, onionAddressExplorerUrl); } - //DEBUG("explorer values: " << txExplorerUrl << " " << addressExplorerUrl << " " << onionTxExplorerUrl << " " << onionAddressExplorerUrl ); + // DEBUG("explorer values: " << txExplorerUrl << " " << addressExplorerUrl << " " << onionTxExplorerUrl << " " << onionAddressExplorerUrl ); DEBUG("onionTxExplorerUrl=" % onionTxExplorerUrl); DEBUG("onionAddressExplorerUrl=" % onionAddressExplorerUrl); return Explorer{txExplorerUrl, addressExplorerUrl, onionTxExplorerUrl, onionAddressExplorerUrl}; } -void Settings::saveExplorer(const QString& txExplorerUrl, const QString& addressExplorerUrl, const QString& onionTxExplorerUrl, const QString& onionAddressExplorerUrl) { +void Settings::saveExplorer(const QString &txExplorerUrl, const QString &addressExplorerUrl, const QString &onionTxExplorerUrl, const QString &onionAddressExplorerUrl) +{ QSettings s; s.setValue("explorer/txExplorerUrl", txExplorerUrl); s.setValue("explorer/addressExplorerUrl", addressExplorerUrl); s.setValue("explorer/onionTxExplorerUrl", onionTxExplorerUrl); s.setValue("explorer/onionAddressExplorerUrl", onionAddressExplorerUrl); - //DEBUG("saving explorer values: " << txExplorerUrl << " " << addressExplorerUrl << " " << onionTxExplorerUrl << " " << onionAddressExplorerUrl ); + // DEBUG("saving explorer values: " << txExplorerUrl << " " << addressExplorerUrl << " " << onionTxExplorerUrl << " " << onionAddressExplorerUrl ); } -Config Settings::getSettings() { +Config Settings::getSettings() +{ // Load from the QT Settings. QSettings s; - auto host = s.value("connection/host").toString(); - auto port = s.value("connection/port").toString(); - auto username = s.value("connection/rpcuser").toString(); - auto password = s.value("connection/rpcpassword").toString(); - + auto host = s.value("connection/host").toString(); + auto port = s.value("connection/port").toString(); + auto username = s.value("connection/rpcuser").toString(); + auto password = s.value("connection/rpcpassword").toString(); + return Config{host, port, username, password}; } -void Settings::saveSettings(const QString& host, const QString& port, const QString& username, const QString& password) { +void Settings::saveSettings(const QString &host, const QString &port, const QString &username, const QString &password) +{ QSettings s; s.setValue("connection/host", host); @@ -103,218 +118,262 @@ void Settings::saveSettings(const QString& host, const QString& port, const QStr init(); } -void Settings::saveRestoreTableHeader(QTableView* table, QDialog* d, QString tablename) { +void Settings::saveRestoreTableHeader(QTableView *table, QDialog *d, QString tablename) +{ table->horizontalHeader()->restoreState(QSettings().value(tablename).toByteArray()); table->horizontalHeader()->setStretchLastSection(true); - QObject::connect(d, &QDialog::finished, [=](auto) { - QSettings().setValue(tablename, table->horizontalHeader()->saveState()); - }); + QObject::connect(d, &QDialog::finished, [=](auto) + { QSettings().setValue(tablename, table->horizontalHeader()->saveState()); }); } -void Settings::setUsingHushConf(QString confLocation) { +void Settings::setUsingHushConf(QString confLocation) +{ if (!confLocation.isEmpty()) _confLocation = confLocation; } -bool Settings::isTestnet() { +bool Settings::isTestnet() +{ return _isTestnet; } -void Settings::setTestnet(bool isTestnet) { +void Settings::setTestnet(bool isTestnet) +{ this->_isTestnet = isTestnet; } -bool Settings::isSaplingAddress(QString addr) { +bool Settings::isSaplingAddress(QString addr) +{ if (!isValidAddress(addr)) return false; - return ( isTestnet() && addr.startsWith("ztestsapling")) || + return (isTestnet() && addr.startsWith("ztestsapling")) || (!isTestnet() && addr.startsWith("zs1")); } -bool Settings::isSproutAddress(QString addr) { +bool Settings::isSproutAddress(QString addr) +{ if (!isValidAddress(addr)) return false; return isZAddress(addr) && !isSaplingAddress(addr); } -bool Settings::isZAddress(QString addr) { +bool Settings::isZAddress(QString addr) +{ if (!isValidAddress(addr)) return false; return addr.startsWith("z"); } -bool Settings::isTAddress(QString addr) { +bool Settings::isTAddress(QString addr) +{ if (!isValidAddress(addr)) return false; return addr.startsWith("R"); } -int Settings::getHushdVersion() { +int Settings::getHushdVersion() +{ return _hushdVersion; } -void Settings::setHushdVersion(int version) { +void Settings::setHushdVersion(int version) +{ _hushdVersion = version; } -bool Settings::isSyncing() { +bool Settings::isSyncing() +{ return _isSyncing; } -void Settings::setSyncing(bool syncing) { +void Settings::setSyncing(bool syncing) +{ this->_isSyncing = syncing; } -int Settings::getBlockNumber() { +int Settings::getBlockNumber() +{ return this->_blockNumber; } -void Settings::setBlockNumber(int number) { +void Settings::setBlockNumber(int number) +{ this->_blockNumber = number; } -bool Settings::isSaplingActive() { - return (isTestnet() && getBlockNumber() > 0) || (!isTestnet() && getBlockNumber() > 0); +bool Settings::isSaplingActive() +{ + return (isTestnet() && getBlockNumber() > 0) || (!isTestnet() && getBlockNumber() > 0); } -double Settings::getHUSHPrice() { +double Settings::getHUSHPrice() +{ return hushPrice; } -double Settings::get_price(QString currency) { +double Settings::get_price(QString currency) +{ currency = currency.toLower(); QString ticker = currency; auto search = prices.find(currency); - if (search != prices.end()) { + if (search != prices.end()) + { qDebug() << "Found price of " << ticker << " = " << search->second; return search->second; - } else { + } + else + { qDebug() << "Could not find price of" << ticker << "!!!"; return 0.0; } } -void Settings::set_price(QString curr, double price) { +void Settings::set_price(QString curr, double price) +{ QString ticker = curr; qDebug() << "Setting price of " << ticker << "=" << QString::number(price); - prices.insert( std::make_pair(curr, price) ); - prices.insert( std::make_pair(curr, price) ); + prices.insert(std::make_pair(curr, price)); + prices.insert(std::make_pair(curr, price)); } -void Settings::set_volume(QString curr, double volume) { +void Settings::set_volume(QString curr, double volume) +{ QString ticker = curr; qDebug() << "Setting volume of " << ticker << "=" << QString::number(volume); - volumes.insert( std::make_pair(curr, volume) ); + volumes.insert(std::make_pair(curr, volume)); } -double Settings::get_volume(QString currency) { +double Settings::get_volume(QString currency) +{ currency = currency.toLower(); QString ticker = currency; auto search = volumes.find(currency); - if (search != volumes.end()) { + if (search != volumes.end()) + { qDebug() << "Found volume of " << ticker << " = " << search->second; return search->second; - } else { + } + else + { qDebug() << "Could not find volume of" << ticker << "!!!"; return 0.0; } } -void Settings::set_marketcap(QString curr, double marketcap) { +void Settings::set_marketcap(QString curr, double marketcap) +{ QString ticker = curr; qDebug() << "Setting marketcap of " << ticker << "=" << QString::number(marketcap); - marketcaps.insert( std::make_pair(curr, marketcap) ); + marketcaps.insert(std::make_pair(curr, marketcap)); } -double Settings::get_marketcap(QString currency) { +double Settings::get_marketcap(QString currency) +{ currency = currency.toLower(); QString ticker = currency; auto search = marketcaps.find(currency); - if (search != marketcaps.end()) { + if (search != marketcaps.end()) + { qDebug() << "Found marketcap of " << ticker << " = " << search->second; return search->second; - } else { + } + else + { qDebug() << "Could not find marketcap of" << ticker << "!!!"; return -1.0; } } -unsigned int Settings::getBTCPrice() { +unsigned int Settings::getBTCPrice() +{ // in satoshis return btcPrice; } -bool Settings::getAutoShield() { +bool Settings::getAutoShield() +{ // Load from Qt settings return QSettings().value("options/autoshield", true).toBool(); } -void Settings::setAutoShield(bool allow) { +void Settings::setAutoShield(bool allow) +{ QSettings().setValue("options/autoshield", allow); } -bool Settings::getAllowCustomFees() { +bool Settings::getAllowCustomFees() +{ // Load from the QT Settings. return QSettings().value("options/customfees", true).toBool(); } -void Settings::setAllowCustomFees(bool allow) { +void Settings::setAllowCustomFees(bool allow) +{ QSettings().setValue("options/customfees", allow); } -QString Settings::get_theme_name() { +QString Settings::get_theme_name() +{ // Load from the QT Settings. QString theme_name = QSettings().value("options/theme_name", false).toString(); - //qDebug() << __func__ << ": theme_name=" << theme_name; + // qDebug() << __func__ << ": theme_name=" << theme_name; return theme_name; } -void Settings::set_theme_name(QString theme_name) { +void Settings::set_theme_name(QString theme_name) +{ qDebug() << __func__ << ": settings theme_name=" << theme_name; QSettings().setValue("options/theme_name", theme_name); } -bool Settings::getSaveZtxs() { +bool Settings::getSaveZtxs() +{ // Load from the QT Settings. return QSettings().value("options/savesenttx", true).toBool(); } -void Settings::setSaveZtxs(bool save) { +void Settings::setSaveZtxs(bool save) +{ QSettings().setValue("options/savesenttx", save); } -void Settings::setPeers(int peers) { +void Settings::setPeers(int peers) +{ _peerConnections = peers; } -int Settings::getPeers() { +int Settings::getPeers() +{ return _peerConnections; } //================================= // Static Stuff //================================= -void Settings::saveRestore(QDialog* d) { +void Settings::saveRestore(QDialog *d) +{ d->restoreGeometry(QSettings().value(d->objectName() % "geometry").toByteArray()); - QObject::connect(d, &QDialog::finished, [=](auto) { - QSettings().setValue(d->objectName() % "geometry", d->saveGeometry()); - }); + QObject::connect(d, &QDialog::finished, [=](auto) + { QSettings().setValue(d->objectName() % "geometry", d->saveGeometry()); }); } -QString Settings::getUSDFormat(double bal) { - //TODO: respect current locale! - return QLocale(QLocale::English).toString(bal * Settings::getInstance()->getHUSHPrice(), 'f', 8) + " " +Settings::getInstance()->get_currency_name(); +QString Settings::getUSDFormat(double bal) +{ + // TODO: respect current locale! + return QLocale(QLocale::English).toString(bal * Settings::getInstance()->getHUSHPrice(), 'f', 8) + " " + Settings::getInstance()->get_currency_name(); } -QString Settings::getDecimalString(double amt) { +QString Settings::getDecimalString(double amt) +{ QString f = QString::number(amt, 'f', 8); - while (f.contains(".") && (f.right(1) == "0" || f.right(1) == ".")) { + while (f.contains(".") && (f.right(1) == "0" || f.right(1) == ".")) + { f = f.left(f.length() - 1); } if (f == "-0") @@ -323,12 +382,14 @@ QString Settings::getDecimalString(double amt) { return f; } -QString Settings::getDisplayFormat(double bal) { +QString Settings::getDisplayFormat(double bal) +{ // This is idiotic. Why doesn't QString have a way to do this? return getDecimalString(bal) % " " % Settings::getTokenName(); } -QString Settings::getHUSHUSDDisplayFormat(double bal) { +QString Settings::getHUSHUSDDisplayFormat(double bal) +{ auto usdFormat = getUSDFormat(bal); if (!usdFormat.isEmpty()) return getDisplayFormat(bal) % " (" % getUSDFormat(bal) % ")"; @@ -338,32 +399,39 @@ QString Settings::getHUSHUSDDisplayFormat(double bal) { const QString Settings::txidStatusMessage = QString(QObject::tr("Transaction submitted (right click to copy) txid:")); -QString Settings::getTokenName() { - if (isdragonx) { - return "DRGX"; +QString Settings::getTokenName() +{ + if (isHSC) + { + return QString::fromStdString(HSC_ticker); } - if (Settings::getInstance()->isTestnet()) { + if (Settings::getInstance()->isTestnet()) + { return "TUSH"; - } else { + } + else + { return "HUSH"; } } -//TODO: this isn't used for donations -QString Settings::getDonationAddr() { - if (Settings::getInstance()->isTestnet()) { - return "ztestsaplingXXX"; +// TODO: this isn't used for donations +QString Settings::getDonationAddr() +{ + if (Settings::getInstance()->isTestnet()) + { + return "ztestsaplingXXX"; } // This is used for user feedback return "zs1aq4xnrkjlnxx0zesqye7jz3dfrf3rjh7q5z6u8l6mwyqqaam3gx3j2fkqakp33v93yavq46j83q"; } -bool Settings::addToHushConf(QString confLocation, QString line) { +bool Settings::addToHushConf(QString confLocation, QString line) +{ QFile file(confLocation); if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) return false; - QTextStream out(&file); out << line << "\n"; file.close(); @@ -371,32 +439,36 @@ bool Settings::addToHushConf(QString confLocation, QString line) { return true; } -QString Settings::get_currency_name() { +QString Settings::get_currency_name() +{ // Load from the QT Settings. return QSettings().value("options/currency_name", "BTC").toString(); } -void Settings::set_currency_name(QString currency_name) { +void Settings::set_currency_name(QString currency_name) +{ QSettings().setValue("options/currency_name", currency_name); } -QString Settings::get_language() { +QString Settings::get_language() +{ // use the default system language if none is set QString locale = QLocale::system().name(); // remove country data, i.e. en_US => en - locale.truncate( locale.lastIndexOf("_")); + locale.truncate(locale.lastIndexOf("_")); auto lang = QSettings().value("options/language", locale).toString(); qDebug() << __func__ << ": found lang=" << lang << " in config file"; return lang; } -void Settings::set_language(QString lang) { +void Settings::set_language(QString lang) +{ qDebug() << __func__ << ": setting lang=" << lang << " in config file"; QSettings().setValue("options/language", lang); } - -bool Settings::removeFromHushConf(QString confLocation, QString option) { +bool Settings::removeFromHushConf(QString confLocation, QString option) +{ if (confLocation.isEmpty()) return false; @@ -407,11 +479,13 @@ bool Settings::removeFromHushConf(QString confLocation, QString option) { QList lines; QTextStream in(&file); - while (!in.atEnd()) { + while (!in.atEnd()) + { QString line = in.readLine(); auto s = line.indexOf("="); QString name = line.left(s).trimmed().toLower(); - if (name != option) { + if (name != option) + { lines.append(line); } } @@ -422,7 +496,8 @@ bool Settings::removeFromHushConf(QString confLocation, QString option) { return false; QTextStream out(&newfile); - for (QString line : lines) { + for (QString line : lines) + { out << line << endl; } newfile.close(); @@ -430,90 +505,115 @@ bool Settings::removeFromHushConf(QString confLocation, QString option) { return true; } -double Settings::getMinerFee() { +double Settings::getMinerFee() +{ return 0.0001; } -bool Settings::isValidSaplingPrivateKey(QString pk) { - if (isTestnet()) { +bool Settings::isValidSaplingPrivateKey(QString pk) +{ + if (isTestnet()) + { QRegExp zspkey("^secret-extended-key-test[0-9a-z]{278}$", Qt::CaseInsensitive); return zspkey.exactMatch(pk); - } else { + } + else + { QRegExp zspkey("^secret-extended-key-main[0-9a-z]{278}$", Qt::CaseInsensitive); return zspkey.exactMatch(pk); } } -bool Settings::isValidAddress(QString addr) { - QRegExp zsexp("^zs1[a-z0-9]{75}$", Qt::CaseInsensitive); +bool Settings::isValidAddress(QString addr) +{ + QRegExp zsexp("^zs1[a-z0-9]{75}$", Qt::CaseInsensitive); QRegExp ztsexp("^ztestsapling[a-z0-9]{76}", Qt::CaseInsensitive); QRegExp texp("^R[a-z0-9]{33}$", Qt::CaseInsensitive); - //qDebug() << "isValidAddress(" << addr << ")"; + // qDebug() << "isValidAddress(" << addr << ")"; - return texp.exactMatch(addr) || ztsexp.exactMatch(addr) || zsexp.exactMatch(addr); + return texp.exactMatch(addr) || ztsexp.exactMatch(addr) || zsexp.exactMatch(addr); } // Get a pretty string representation of this Payment URI -QString Settings::paymentURIPretty(PaymentURI uri) { - return QString() + "Payment Request\n" + "Pay: " + uri.addr + "\nAmount: " + getDisplayFormat(uri.amt.toDouble()) - + "\nMemo:" + QUrl::fromPercentEncoding(uri.memo.toUtf8()); +QString Settings::paymentURIPretty(PaymentURI uri) +{ + return QString() + "Payment Request\n" + "Pay: " + uri.addr + "\nAmount: " + getDisplayFormat(uri.amt.toDouble()) + "\nMemo:" + QUrl::fromPercentEncoding(uri.memo.toUtf8()); } // Parse a payment URI string into its components -PaymentURI Settings::parseURI(QString uri) { +PaymentURI Settings::parseURI(QString uri) +{ PaymentURI ans; - auto proto=""; - if (isdragonx) { - proto ="drgx:"; - if (!uri.startsWith(proto % QString(":"))) { - ans.error = "Not a DRGX payment URI"; + auto proto = ""; + if (isHSC) + { + std::string ticker = HSC_ticker + ":"; + proto = ((ticker.c_str())); + if (!uri.startsWith(proto % QString(":"))) + { + + std::string not_a_uri = "Not a " + HSC_ticker + " payment URI"; + ans.error = (not_a_uri.c_str()); return ans; } - } else { + } + else + { proto = "hush:"; - if (!uri.startsWith(proto % QString(":"))) { + if (!uri.startsWith(proto % QString(":"))) + { ans.error = "Not a HUSH payment URI"; return ans; } } - uri = uri.right(uri.length() - QString("hush:").length()); - if(isdragonx) { - uri = uri.right(uri.length() - QString("drgx:").length()); + if (isHSC) + { + uri = uri.right((uri.length() - QString(QString::fromStdString(HSC_ticker) % ":").length())); } QRegExp re("([a-zA-Z0-9]+)"); int pos; - if ( (pos = re.indexIn(uri)) == -1 ) { + if ((pos = re.indexIn(uri)) == -1) + { ans.error = "Couldn't find an address"; return ans; } ans.addr = re.cap(1); - if (!Settings::isValidAddress(ans.addr)) { + if (!Settings::isValidAddress(ans.addr)) + { ans.error = "Could not understand address"; return ans; } - uri = uri.right(uri.length() - ans.addr.length()-1); // swallow '?' + uri = uri.right(uri.length() - ans.addr.length() - 1); // swallow '?' QUrlQuery query(uri); - + // parse out amt / amount - if (query.hasQueryItem("amt")) { - ans.amt = query.queryItemValue("amt"); - } else if (query.hasQueryItem("amount")) { - ans.amt = query.queryItemValue("amount"); + if (query.hasQueryItem("amt")) + { + ans.amt = query.queryItemValue("amt"); + } + else if (query.hasQueryItem("amount")) + { + ans.amt = query.queryItemValue("amount"); } - + // parse out memo / msg / message - if (query.hasQueryItem("memo")) { - ans.memo = query.queryItemValue("memo"); - } else if (query.hasQueryItem("msg")) { - ans.memo = query.queryItemValue("msg"); - } else if (query.hasQueryItem("message")) { - ans.memo = query.queryItemValue("message"); + if (query.hasQueryItem("memo")) + { + ans.memo = query.queryItemValue("memo"); + } + else if (query.hasQueryItem("msg")) + { + ans.memo = query.queryItemValue("msg"); + } + else if (query.hasQueryItem("message")) + { + ans.memo = query.queryItemValue("message"); } return ans; diff --git a/src/txtablemodel.cpp b/src/txtablemodel.cpp index 0fdddf2..c9614fb 100644 --- a/src/txtablemodel.cpp +++ b/src/txtablemodel.cpp @@ -5,21 +5,26 @@ #include "rpc.h" #include "guiconstants.h" -extern bool isdragonx; +extern bool isHSC; +extern std::string HSC_ticker; +extern std::string HSC_default_theme; TxTableModel::TxTableModel(QObject *parent) - : QAbstractTableModel(parent) { + : QAbstractTableModel(parent) +{ headers << QObject::tr("Type") << QObject::tr("Address") << QObject::tr("Date/Time") << QObject::tr("Amount"); } -TxTableModel::~TxTableModel() { +TxTableModel::~TxTableModel() +{ delete modeldata; delete tTrans; delete zsTrans; delete zrTrans; } -void TxTableModel::addZSentData(const QList& data) { +void TxTableModel::addZSentData(const QList &data) +{ delete zsTrans; zsTrans = new QList(); std::copy(data.begin(), data.end(), std::back_inserter(*zsTrans)); @@ -27,7 +32,8 @@ void TxTableModel::addZSentData(const QList& data) { updateAllData(); } -void TxTableModel::addZRecvData(const QList& data) { +void TxTableModel::addZRecvData(const QList &data) +{ delete zrTrans; zrTrans = new QList(); std::copy(data.begin(), data.end(), std::back_inserter(*zrTrans)); @@ -35,8 +41,8 @@ void TxTableModel::addZRecvData(const QList& data) { updateAllData(); } - -void TxTableModel::addTData(const QList& data) { +void TxTableModel::addTData(const QList &data) +{ delete tTrans; tTrans = new QList(); std::copy(data.begin(), data.end(), std::back_inserter(*tTrans)); @@ -44,7 +50,8 @@ void TxTableModel::addTData(const QList& data) { updateAllData(); } -bool TxTableModel::exportToCsv(QString fileName) const { +bool TxTableModel::exportToCsv(QString fileName) const +{ if (!modeldata) return false; @@ -52,18 +59,21 @@ bool TxTableModel::exportToCsv(QString fileName) const { if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) return false; - QTextStream out(&file); // we will serialize the data into the file + QTextStream out(&file); // we will serialize the data into the file // Write headers - for (int i = 0; i < headers.length(); i++) { + for (int i = 0; i < headers.length(); i++) + { out << "\"" << headers[i] << "\","; } out << "\"Memo\""; out << endl; - + // Write out each row - for (int row = 0; row < modeldata->length(); row++) { - for (int col = 0; col < headers.length(); col++) { + for (int row = 0; row < modeldata->length(); row++) + { + for (int col = 0; col < headers.length(); col++) + { out << "\"" << data(index(row, col), Qt::DisplayRole).toString() << "\","; } // Memo @@ -75,33 +85,38 @@ bool TxTableModel::exportToCsv(QString fileName) const { return true; } -void TxTableModel::updateAllData() { +void TxTableModel::updateAllData() +{ auto newmodeldata = new QList(); - if (tTrans != nullptr) std::copy( tTrans->begin(), tTrans->end(), std::back_inserter(*newmodeldata)); - if (zsTrans != nullptr) std::copy(zsTrans->begin(), zsTrans->end(), std::back_inserter(*newmodeldata)); - if (zrTrans != nullptr) std::copy(zrTrans->begin(), zrTrans->end(), std::back_inserter(*newmodeldata)); + if (tTrans != nullptr) + std::copy(tTrans->begin(), tTrans->end(), std::back_inserter(*newmodeldata)); + if (zsTrans != nullptr) + std::copy(zsTrans->begin(), zsTrans->end(), std::back_inserter(*newmodeldata)); + if (zrTrans != nullptr) + std::copy(zrTrans->begin(), zrTrans->end(), std::back_inserter(*newmodeldata)); // Sort by reverse time - std::sort(newmodeldata->begin(), newmodeldata->end(), [=] (auto a, auto b) { - return a.datetime > b.datetime; // reverse sort - }); + std::sort(newmodeldata->begin(), newmodeldata->end(), [=](auto a, auto b) + { + return a.datetime > b.datetime; // reverse sort + }); // And then swap out the modeldata with the new one. delete modeldata; modeldata = newmodeldata; - dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1)); + dataChanged(index(0, 0), index(modeldata->size() - 1, columnCount(index(0, 0)) - 1)); layoutChanged(); } - -QImage TxTableModel::colorizeIcon(QIcon icon, QColor color) const{ +QImage TxTableModel::colorizeIcon(QIcon icon, QColor color) const +{ QImage img(icon.pixmap(16, 16).toImage()); img = img.convertToFormat(QImage::Format_ARGB32); - for (int x = img.width(); x--; ) + for (int x = img.width(); x--;) { - for (int y = img.height(); y--; ) + for (int y = img.height(); y--;) { const QRgb rgb = img.pixel(x, y); img.setPixel(x, y, qRgba(color.red(), color.green(), color.blue(), qAlpha(rgb))); @@ -110,123 +125,158 @@ QImage TxTableModel::colorizeIcon(QIcon icon, QColor color) const{ return img; } - -int TxTableModel::rowCount(const QModelIndex&) const +int TxTableModel::rowCount(const QModelIndex &) const { - if (modeldata == nullptr) return 0; + if (modeldata == nullptr) + return 0; return modeldata->size(); } -int TxTableModel::columnCount(const QModelIndex&) const +int TxTableModel::columnCount(const QModelIndex &) const { return headers.size(); } - - QVariant TxTableModel::data(const QModelIndex &index, int role) const - { +QVariant TxTableModel::data(const QModelIndex &index, int role) const +{ // Get current theme name QString theme_name = Settings::getInstance()->get_theme_name(); QBrush b; QColor color; - if (theme_name == "dark" || theme_name == "midnight") { + if (theme_name == "dark" || theme_name == "midnight") + { color = COLOR_WHITE; - }else if(theme_name == "dragonx"){ + } + else if (theme_name == (QString::fromStdString(HSC_default_theme))) + { color = COLOR_DRAGONX_TEXT; - }else{ + } + else + { color = COLOR_BLACK; } // Align column 4 (amount) right - if (role == Qt::TextAlignmentRole && index.column() == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter); - - if (role == Qt::ForegroundRole) { - if (modeldata->at(index.row()).confirmations == 0) { + if (role == Qt::TextAlignmentRole && index.column() == 3) + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + + if (role == Qt::ForegroundRole) + { + if (modeldata->at(index.row()).confirmations == 0) + { b.setColor(COLOR_UNCONFIRMED_TX); return b; } - if (theme_name == "dark" || theme_name == "midnight") { + if (theme_name == "dark" || theme_name == "midnight") + { b.setColor(color); return b; - }else{ + } + else + { b.setColor(color); return b; } - return b; + return b; } auto dat = modeldata->at(index.row()); - if (role == Qt::DisplayRole) { - switch (index.column()) { - case 0: return dat.type; - case 1: { - auto addr = modeldata->at(index.row()).address; - if (addr.trimmed().isEmpty()) - return "(Shielded)"; - else - return addr; - } - case 2: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString(); - case 3: return Settings::getDisplayFormat(modeldata->at(index.row()).amount); + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case 0: + return dat.type; + case 1: + { + auto addr = modeldata->at(index.row()).address; + if (addr.trimmed().isEmpty()) + return "(Shielded)"; + else + return addr; + } + case 2: + return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString(); + case 3: + return Settings::getDisplayFormat(modeldata->at(index.row()).amount); } - } - - if (role == Qt::ToolTipRole) { - switch (index.column()) { - case 0: { - if (dat.memo.startsWith("hush:")) { - return Settings::paymentURIPretty(Settings::parseURI(dat.memo)); - } else { - return modeldata->at(index.row()).type + - (dat.memo.isEmpty() ? "" : " tx memo: \"" + dat.memo.toHtmlEscaped() + "\""); - } - } - case 1: { - auto addr = modeldata->at(index.row()).address; - if (addr.trimmed().isEmpty()) - return "(Shielded)"; - else - return addr; - } - case 2: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString(); - case 3: return Settings::getInstance()->getUSDFormat(modeldata->at(index.row()).amount); - } } - if (role == Qt::DecorationRole && index.column() == 0) { + if (role == Qt::ToolTipRole) + { + switch (index.column()) + { + case 0: + { + if (dat.memo.startsWith("hush:")) + { + return Settings::paymentURIPretty(Settings::parseURI(dat.memo)); + } + else + { + return modeldata->at(index.row()).type + + (dat.memo.isEmpty() ? "" : " tx memo: \"" + dat.memo.toHtmlEscaped() + "\""); + } + } + case 1: + { + auto addr = modeldata->at(index.row()).address; + if (addr.trimmed().isEmpty()) + return "(Shielded)"; + else + return addr; + } + case 2: + return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString(); + case 3: + return Settings::getInstance()->getUSDFormat(modeldata->at(index.row()).amount); + } + } + + if (role == Qt::DecorationRole && index.column() == 0) + { - //qDebug() << "TX Type = " + dat.type; + // qDebug() << "TX Type = " + dat.type; - if (!dat.memo.isEmpty()) { + if (!dat.memo.isEmpty()) + { // If the memo is a Payment URI, then show a payment request icon - if(isdragonx) { - if (dat.memo.startsWith("drgx:")) { + if (isHSC) + { + std::string ticker = "" + HSC_ticker + ":"; + if (dat.memo.startsWith(ticker.c_str())) + { QIcon icon(":/icons/paymentreq.gif"); return QVariant(icon.pixmap(16, 16)); } - } else if (dat.memo.startsWith("hush:")) { + } + else if (dat.memo.startsWith("hush:")) + { QIcon icon(":/icons/paymentreq.gif"); return QVariant(icon.pixmap(16, 16)); } // Return the info pixmap to indicate memo - QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); return QVariant(icon.pixmap(16, 16)); - } else { + } + else + { // TODO: Add appropriate icons for types of txs instead of empty pixmap - //qDebug() << "Type = " +getType(index.row()) + "Address = " +getAddr(index.row()) + "From Address = " +getFromAddr(index.row()); + // qDebug() << "Type = " +getType(index.row()) + "Address = " +getAddr(index.row()) + "From Address = " +getFromAddr(index.row()); // Send - if(this->getType(index.row()) == "send"){ + if (this->getType(index.row()) == "send") + { QImage image = colorizeIcon(QIcon(":/icons/tx_output.png"), color); QIcon icon; icon.addPixmap(QPixmap::fromImage(image)); return QVariant(icon.pixmap(16, 16)); - } // Send T->Z - Untested - if(this->getType(index.row()) == "send" && !this->getFromAddr(index.row()).startsWith("zs1")){ + if (this->getType(index.row()) == "send" && !this->getFromAddr(index.row()).startsWith("zs1")) + { QImage image = colorizeIcon(QIcon(":/icons/lock_closed.png"), color); QIcon icon; icon.addPixmap(QPixmap::fromImage(image)); @@ -234,7 +284,8 @@ int TxTableModel::columnCount(const QModelIndex&) const } // Receive - if(this->getType(index.row()) == "receive"){ + if (this->getType(index.row()) == "receive") + { QImage image = colorizeIcon(QIcon(":/icons/tx_input.png"), color); QIcon icon; icon.addPixmap(QPixmap::fromImage(image)); @@ -242,7 +293,8 @@ int TxTableModel::columnCount(const QModelIndex&) const } // Mined - if(this->getType(index.row()) == "generate"){ + if (this->getType(index.row()) == "generate") + { QImage image = colorizeIcon(QIcon(":/icons/tx_mined.png"), color); QIcon icon; icon.addPixmap(QPixmap::fromImage(image)); @@ -257,54 +309,64 @@ int TxTableModel::columnCount(const QModelIndex&) const } return QVariant(); - } - +} - QVariant TxTableModel::headerData(int section, Qt::Orientation orientation, int role) const - { - if (role == Qt::TextAlignmentRole && section == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter); +QVariant TxTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::TextAlignmentRole && section == 3) + return QVariant(Qt::AlignRight | Qt::AlignVCenter); - if (role == Qt::FontRole) { - QFont f; - f.setBold(true); - return f; - } + if (role == Qt::FontRole) + { + QFont f; + f.setBold(true); + return f; + } - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - return headers.at(section); - } + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + return headers.at(section); + } - return QVariant(); - } + return QVariant(); +} -QString TxTableModel::getTxId(int row) const { +QString TxTableModel::getTxId(int row) const +{ return modeldata->at(row).txid; } -QString TxTableModel::getMemo(int row) const { +QString TxTableModel::getMemo(int row) const +{ return modeldata->at(row).memo; } -qint64 TxTableModel::getConfirmations(int row) const { +qint64 TxTableModel::getConfirmations(int row) const +{ return modeldata->at(row).confirmations; } -QString TxTableModel::getAddr(int row) const { +QString TxTableModel::getAddr(int row) const +{ return modeldata->at(row).address.trimmed(); } -QString TxTableModel::getFromAddr(int row) const { +QString TxTableModel::getFromAddr(int row) const +{ return modeldata->at(row).fromAddr.trimmed(); } -qint64 TxTableModel::getDate(int row) const { +qint64 TxTableModel::getDate(int row) const +{ return modeldata->at(row).datetime; } -QString TxTableModel::getType(int row) const { +QString TxTableModel::getType(int row) const +{ return modeldata->at(row).type; } -QString TxTableModel::getAmt(int row) const { +QString TxTableModel::getAmt(int row) const +{ return Settings::getDecimalString(modeldata->at(row).amount); }