From 1b4ced8c8252956014e7a0f8517223599d9d9842 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Thu, 1 Nov 2018 14:10:26 -0700 Subject: [PATCH] Manage connection --- src/connection.cpp | 133 ++++++++++++++++++++++++++++++--------------- src/connection.h | 29 +++++++--- src/connection.ui | 47 +++++++++++++--- src/mainwindow.cpp | 28 +++------- src/rpc.cpp | 50 ++++++++++++++--- src/rpc.h | 7 ++- src/settings.cpp | 106 ++---------------------------------- src/settings.h | 29 +++------- src/settings.ui | 6 +- 9 files changed, 221 insertions(+), 214 deletions(-) diff --git a/src/connection.cpp b/src/connection.cpp index 089a384..aefcb30 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -7,7 +7,7 @@ #include "precompiled.h" using json = nlohmann::json; - +/* class LoadingDialog : public QDialog { //Q_OBJECT public: @@ -22,11 +22,12 @@ LoadingDialog::~LoadingDialog() {} void LoadingDialog::reject() { //event->ignore(); } - -ConnectionLoader::ConnectionLoader(MainWindow* main) { +*/ +ConnectionLoader::ConnectionLoader(MainWindow* main, RPC* rpc) { this->main = main; + this->rpc = rpc; - d = new LoadingDialog(main); + d = new QDialog(main); connD = new Ui_ConnectionDialog(); connD->setupUi(d); @@ -44,84 +45,124 @@ ConnectionLoader::~ConnectionLoader() { delete connD; } -void ConnectionLoader::getConnection(std::function cb) { - +void ConnectionLoader::loadConnection() { // Priority 1: Try to connect to detect zcash.conf and connect to it. bool isZcashConfPresent = false; - auto conn = autoDetectZcashConf(); + auto config = autoDetectZcashConf(); // If not autodetected, go and read the UI Settings - if (conn.get() != nullptr) { + if (config.get() != nullptr) { isZcashConfPresent = true; } else { - conn = loadFromSettings(); + config = loadFromSettings(); - if (conn.get() == nullptr) { + if (config.get() == nullptr) { // Nothing configured, show an error auto explanation = QString() % "A zcash.conf was not found on this machine.\n\n" % "If you are connecting to a remote/non-standard node " % "please set the host/port and user/password in the File->Settings menu."; - QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); - connD->icon->setPixmap(icon.pixmap(64, 64)); - connD->status->setText(explanation); - connD->progressBar->setValue(0); + showError(explanation); + rpc->setConnection(nullptr); - connD->buttonBox->setEnabled(true); - cb(nullptr); + return; } } + auto connection = makeConnection(config); + refreshZcashdState(connection); +} + +Connection* ConnectionLoader::makeConnection(std::shared_ptr config) { QNetworkAccessManager* client = new QNetworkAccessManager(main); QUrl myurl; myurl.setScheme("http"); - myurl.setHost(Settings::getInstance()->getHost()); - myurl.setPort(Settings::getInstance()->getPort().toInt()); + myurl.setHost(config.get()->host); + myurl.setPort(config.get()->port.toInt()); QNetworkRequest* request = new QNetworkRequest(); request->setUrl(myurl); request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); - QString headerData = "Basic " + Settings::getInstance()->getUsernamePassword().toLocal8Bit().toBase64(); - request->setRawHeader("Authorization", headerData.toLocal8Bit()); + QString userpass = config.get()->rpcuser % ":" % config.get()->rpcpassword; + QString headerData = "Basic " + userpass.toLocal8Bit().toBase64(); + request->setRawHeader("Authorization", headerData.toLocal8Bit()); + + return new Connection(client, request, config); +} - auto connection = new Connection(client, request); +void ConnectionLoader::refreshZcashdState(Connection* connection) { json payload = { {"jsonrpc", "1.0"}, {"id", "someid"}, {"method", "getinfo"} }; connection->doRPC(payload, - [=] (auto result) { + [=] (auto) { // Success - d->close(); - cb(new RPC(connection, main)); + d->hide(); + rpc->setConnection(connection); }, [=] (auto err, auto res) { - // Failed - auto explanation = QString() - % (isZcashConfPresent ? "A zcash.conf file was found, but a" : "A") - % " connection to zcashd could not be established.\n\n" - % "If you are connecting to a remote/non-standard node " - % "please set the host/port and user/password in the File->Settings menu."; - - QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); - connD->icon->setPixmap(icon.pixmap(64, 64)); - connD->status->setText(explanation); - connD->progressBar->setValue(0); - - connD->buttonBox->setEnabled(true); + // Failed, see what it is. + qDebug() << err << ":" << QString::fromStdString(res.dump()); + + if (err == QNetworkReply::NetworkError::ConnectionRefusedError) { + auto isZcashConfFound = connection->config.get()->usingZcashConf; + auto explanation = QString() + % (isZcashConfFound ? "A zcash.conf file was found, but a" : "A") + % " connection to zcashd could not be established.\n\n" + % "If you are connecting to a remote/non-standard node " + % "please set the host/port and user/password in the File->Settings menu"; + + showError(explanation); + } else if (err == QNetworkReply::NetworkError::AuthenticationRequiredError) { + auto explanation = QString() + % "Authentication failed. The username / password you specified was " + % "not accepted by zcashd. Try changing it in the File->Settings menu"; + + showError(explanation); + } else if (err == QNetworkReply::NetworkError::InternalServerError && !res.is_discarded()) { + // The server is loading, so just poll until it succeeds + QString status = QString::fromStdString(res["error"]["message"]); + + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + connD->icon->setPixmap(icon.pixmap(128, 128)); + connD->status->setText(status); + + // Refresh after one second + QTimer::singleShot(1000, [=]() { this->refreshZcashdState(connection); }); + } } ); } +void ConnectionLoader::showError(QString explanation) { + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); + connD->icon->setPixmap(icon.pixmap(128, 128)); + connD->status->setText(explanation); + connD->title->setText(""); + + connD->buttonBox->setEnabled(true); +} + +/* +int ConnectionLoader::getProgressFromStatus(QString status) { + if (status.startsWith("Loading block")) return 20; + if (status.startsWith("Verifying")) return 40; + if (status.startsWith("Loading Wallet")) return 60; + if (status.startsWith("Activating")) return 80; + if (status.startsWith("Rescanning")) return 90; + return 0; +} +*/ /** * Try to automatically detect a zcash.conf file in the correct location and load parameters */ -std::shared_ptr ConnectionLoader::autoDetectZcashConf() { +std::shared_ptr ConnectionLoader::autoDetectZcashConf() { #ifdef Q_OS_LINUX auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".zcash/zcash.conf"); @@ -149,6 +190,9 @@ std::shared_ptr ConnectionLoader::autoDetectZcashConf() { auto zcashconf = new ConnectionConfig(); zcashconf->host = "127.0.0.1"; zcashconf->connType = ConnectionType::DetectedConfExternalZcashD; + zcashconf->usingZcashConf = true; + + Settings::getInstance()->setUsingZcashConf(confLocation); while (!in.atEnd()) { QString line = in.readLine(); @@ -177,13 +221,13 @@ std::shared_ptr ConnectionLoader::autoDetectZcashConf() { file.close(); - return std::make_shared(zcashconf); + return std::shared_ptr(zcashconf); } /** * Load connection settings from the UI, which indicates an unknown, external zcashd */ -std::shared_ptr ConnectionLoader::loadFromSettings() { +std::shared_ptr ConnectionLoader::loadFromSettings() { // Load from the QT Settings. QSettings s; @@ -195,9 +239,9 @@ std::shared_ptr ConnectionLoader::loadFromSettings() { if (username.isEmpty() || password.isEmpty()) return nullptr; - auto uiConfig = new ConnectionConfig{ host, port, username, password, ConnectionType::UISettingsZCashD }; + auto uiConfig = new ConnectionConfig{ host, port, username, password, false, ConnectionType::UISettingsZCashD }; - return std::make_shared(uiConfig); + return std::shared_ptr(uiConfig); } @@ -207,9 +251,10 @@ std::shared_ptr ConnectionLoader::loadFromSettings() { -Connection::Connection(QNetworkAccessManager* c, QNetworkRequest* r) { +Connection::Connection(QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr conf) { this->restclient = c; - this->request = r; + this->request = r; + this->config = conf; } Connection::~Connection() { diff --git a/src/connection.h b/src/connection.h index 3c329fd..7664e1e 100644 --- a/src/connection.h +++ b/src/connection.h @@ -20,27 +20,37 @@ struct ConnectionConfig { QString port; QString rpcuser; QString rpcpassword; + bool usingZcashConf; ConnectionType connType; }; -class LoadingDialog; +class Connection; class ConnectionLoader { public: - ConnectionLoader(MainWindow* main); + ConnectionLoader(MainWindow* main, RPC* rpc); ~ConnectionLoader(); - void getConnection(std::function cb); + void loadConnection(); private: - std::shared_ptr autoDetectZcashConf(); - std::shared_ptr loadFromSettings(); + std::shared_ptr autoDetectZcashConf(); + std::shared_ptr loadFromSettings(); - LoadingDialog* d; + Connection* makeConnection(std::shared_ptr config); + + void refreshZcashdState(Connection* connection); + int getProgressFromStatus(QString status); + + void showError(QString explanation); + + QDialog* d; Ui_ConnectionDialog* connD; + MainWindow* main; + RPC* rpc; }; /** @@ -49,12 +59,13 @@ private: */ class Connection { public: - Connection(QNetworkAccessManager* c, QNetworkRequest* r); + Connection(QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr conf); ~Connection(); - QNetworkAccessManager* restclient; - QNetworkRequest* request; + QNetworkAccessManager* restclient; + QNetworkRequest* request; + std::shared_ptr config; void doRPC(const json& payload, const std::function& cb, const std::function& ne); diff --git a/src/connection.ui b/src/connection.ui index 7c83748..d213b13 100644 --- a/src/connection.ui +++ b/src/connection.ui @@ -7,7 +7,7 @@ 0 0 513 - 286 + 201 @@ -17,40 +17,69 @@ true - + + + + Your zcashd is still loading. Please wait... + + + + Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Close - + Connection Status + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + true - + TextLabel + + Qt::AlignCenter + + + 20 + - - - - 24 + + + + Qt::Horizontal + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c5ac6c2..d266806 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -63,15 +63,9 @@ MainWindow::MainWindow(QWidget *parent) : setupBalancesTab(); setupTurnstileDialog(); - restoreSavedStates(); + rpc = new RPC(this); - new ConnectionLoader(this).getConnection([=] (RPC* rpc) { - if (rpc == nullptr) - return; - this->rpc = rpc; - this->rpc->refreshZECPrice(); - this->rpc->refresh(true); // Force refresh first time - }); + restoreSavedStates(); } @@ -356,10 +350,11 @@ void MainWindow::setupSettingsModal() { settings.port->setValidator(&validator); // Load current values into the dialog - settings.hostname->setText(Settings::getInstance()->getHost()); - settings.port->setText(Settings::getInstance()->getPort()); - settings.rpcuser->setText(Settings::getInstance()->getUsernamePassword().split(":")[0]); - settings.rpcpassword->setText(Settings::getInstance()->getUsernamePassword().split(":")[1]); + auto conf = Settings::getInstance()->getSettings(); + settings.hostname->setText(conf.host); + settings.port->setText(conf.port); + settings.rpcuser->setText(conf.rpcuser); + settings.rpcpassword->setText(conf.rpcpassword); // If values are coming from zcash.conf, then disable all the fields auto zcashConfLocation = Settings::getInstance()->getZcashdConfLocation(); @@ -390,13 +385,8 @@ void MainWindow::setupSettingsModal() { settings.rpcuser->text(), settings.rpcpassword->text()); - auto me = this; - ConnectionLoader(this).getConnection([&me] (auto newrpc) { - delete me->rpc; - me->rpc = newrpc; - // Then refresh everything. - me->rpc->refresh(true); - }); + auto cl = new ConnectionLoader(this, rpc); + cl->loadConnection(); } }; }); diff --git a/src/rpc.cpp b/src/rpc.cpp index 77a2fd2..ba05f0e 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -6,8 +6,10 @@ using json = nlohmann::json; -RPC::RPC(Connection* conn, MainWindow* main) { - this->conn = conn; +RPC::RPC(MainWindow* main) { + auto cl = new ConnectionLoader(main, this); + cl->loadConnection(); + this->main = main; this->ui = main->ui; @@ -16,15 +18,11 @@ RPC::RPC(Connection* conn, MainWindow* main) { // Setup balances table model balancesTableModel = new BalancesTableModel(main->ui->balancesTable); main->ui->balancesTable->setModel(balancesTableModel); - main->ui->balancesTable->setColumnWidth(0, 300); // Setup transactions table model transactionsTableModel = new TxTableModel(ui->transactionsTable); main->ui->transactionsTable->setModel(transactionsTableModel); main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); - main->ui->transactionsTable->setColumnWidth(1, 350); - main->ui->transactionsTable->setColumnWidth(2, 200); - main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); // Set up timer to refresh Price priceTimer = new QTimer(main); @@ -64,6 +62,14 @@ RPC::~RPC() { delete conn; } +void RPC::setConnection(Connection* c) { + if (c == nullptr) return; + + delete conn; + this->conn = c; + + refresh(); +} void RPC::doRPC(const json& payload, const std::function& cb) { QNetworkReply *reply = conn->restclient->post(*conn->request, QByteArray::fromStdString(payload.dump())); @@ -349,8 +355,14 @@ void RPC::fillTxJsonParams(json& params, Tx tx) { } +void RPC::noConnection() { + ui->statusBar->showMessage("No Connection to zcashd"); +} + // Refresh received z txs by calling z_listreceivedbyaddress/gettransaction 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()) { @@ -459,11 +471,17 @@ void RPC::refreshReceivedZTrans(QList zaddrs) { /// This will refresh all the balance data from zcashd void RPC::refresh(bool force) { + if (conn == nullptr) + return noConnection(); + getInfoThenRefresh(force); } void RPC::getInfoThenRefresh(bool force) { + if (conn == nullptr) + return noConnection(); + json payload = { {"jsonrpc", "1.0"}, {"id", "someid"}, @@ -526,6 +544,9 @@ void RPC::getInfoThenRefresh(bool force) { } void RPC::refreshAddresses() { + if (conn == nullptr) + return noConnection(); + delete zaddresses; zaddresses = new QList(); @@ -591,6 +612,9 @@ bool RPC::processUnspent(const json& reply) { }; void RPC::refreshBalances() { + if (conn == nullptr) + return noConnection(); + // 1. Get the Balances getBalance([=] (json reply) { auto balT = QString::fromStdString(reply["transparent"]).toDouble(); @@ -626,6 +650,9 @@ void RPC::refreshBalances() { } void RPC::refreshTransactions() { + if (conn == nullptr) + return noConnection(); + getTransactions([=] (json reply) { QList txdata; @@ -654,6 +681,9 @@ void RPC::refreshTransactions() { // Read sent Z transactions from the file. void RPC::refreshSentZTrans() { + if (conn == nullptr) + return noConnection(); + auto sentZTxs = SentTxStore::readSentTxFile(); QList txids; @@ -694,13 +724,16 @@ void RPC::refreshSentZTrans() { ); } -void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) { +void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) { watchingOps.insert(newOpid, tx); watchTxStatus(); } void RPC::watchTxStatus() { + if (conn == nullptr) + return noConnection(); + // Make an RPC to load pending operation statues json payload = { {"jsonrpc", "1.0"}, @@ -767,6 +800,9 @@ void RPC::watchTxStatus() { // Get the ZEC->USD price from coinmarketcap using their API void RPC::refreshZECPrice() { + if (conn == nullptr) + return noConnection(); + QUrl cmcURL("https://api.coinmarketcap.com/v1/ticker/"); QNetworkRequest req; diff --git a/src/rpc.h b/src/rpc.h index 887451e..a1f94c4 100644 --- a/src/rpc.h +++ b/src/rpc.h @@ -28,9 +28,11 @@ struct TransactionItem { class RPC { public: - RPC(Connection* conn, MainWindow* main); + RPC(MainWindow* main); ~RPC(); + void setConnection(Connection* c); + void refresh(bool force = false); void refreshAddresses(); @@ -49,7 +51,6 @@ public: void newZaddr(bool sapling, const std::function& cb); void newTaddr(const std::function& cb); - void getZPrivKey(QString addr, const std::function& cb); void getTPrivKey(QString addr, const std::function& cb); void importZPrivKey(QString addr, bool rescan, const std::function& cb); @@ -107,6 +108,8 @@ public: private: + void noConnection(); + void doRPC (const json& payload, const std::function& cb); void doSendRPC(const json& payload, const std::function& cb); void doSendRPC(const json& payload, const std::function& cb, const std::function& err); diff --git a/src/settings.cpp b/src/settings.cpp index cb60b74..fa7fa9a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -6,9 +6,6 @@ Settings* Settings::instance = nullptr; Settings::~Settings() { - delete defaults; - delete zcashconf; - delete uisettings; } bool Settings::getSaveZtxs() { @@ -24,27 +21,6 @@ Settings* Settings::init() { if (instance == nullptr) instance = new Settings(); - // There are 3 possible configurations - // 1. The defaults - instance->defaults = new Config{ "127.0.0.1", "8232", "", "" }; - - // 2. From the UI settings - auto settingsFound = instance->loadFromSettings(); - - // 3. From the zcash.conf file - auto confFound = instance->loadFromFile(); - - // zcash.conf (#3) is first priority if it exists - if (confFound) { - instance->currentConfig = instance->zcashconf; - } - else if (settingsFound) { - instance->currentConfig = instance->uisettings; - } - else { - instance->currentConfig = instance->defaults; - } - return instance; } @@ -52,23 +28,7 @@ Settings* Settings::getInstance() { return instance; } - -QString Settings::getHost() { - return currentConfig->host; -} - -QString Settings::getPort() { - return currentConfig->port; -} - - -QString Settings::getUsernamePassword() { - return currentConfig->rpcuser % ":" % currentConfig->rpcpassword; -} - -bool Settings::loadFromSettings() { - delete uisettings; - +Config Settings::getSettings() { // Load from the QT Settings. QSettings s; @@ -77,9 +37,7 @@ bool Settings::loadFromSettings() { auto username = s.value("connection/rpcuser").toString(); auto password = s.value("connection/rpcpassword").toString(); - uisettings = new Config{host, port, username, password}; - - return !username.isEmpty(); + return Config{host, port, username, password}; } void Settings::saveSettings(const QString& host, const QString& port, const QString& username, const QString& password) { @@ -96,63 +54,9 @@ void Settings::saveSettings(const QString& host, const QString& port, const QStr init(); } -bool Settings::loadFromFile() { - delete zcashconf; - -#ifdef Q_OS_LINUX - confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".zcash/zcash.conf"); -#elif defined(Q_OS_DARWIN) - confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "/Library/Application Support/Zcash/zcash.conf"); -#else - confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Zcash/zcash.conf"); -#endif - - confLocation = QDir::cleanPath(confLocation); - - if (confLocation.isNull()) { - // No zcash file, just return with nothing - return false; - } - - QFile file(confLocation); - if (!file.open(QIODevice::ReadOnly)) { - qDebug() << file.errorString(); - return false; - } - - QTextStream in(&file); - - zcashconf = new Config(); - zcashconf->host = defaults->host; - - while (!in.atEnd()) { - QString line = in.readLine(); - auto s = line.indexOf("="); - QString name = line.left(s).trimmed().toLower(); - QString value = line.right(line.length() - s - 1).trimmed(); - - if (name == "rpcuser") { - zcashconf->rpcuser = value; - } - if (name == "rpcpassword") { - zcashconf->rpcpassword = value; - } - if (name == "rpcport") { - zcashconf->port = value; - } - if (name == "testnet" && - value == "1" && - zcashconf->port.isEmpty()) { - zcashconf->port = "18232"; - } - } - - // If rpcport is not in the file, and it was not set by the testnet=1 flag, then go to default - if (zcashconf->port.isEmpty()) zcashconf->port = defaults->port; - - file.close(); - - return true; +void Settings::setUsingZcashConf(QString confLocation) { + if (!confLocation.isEmpty()) + _confLocation = confLocation; } bool Settings::isTestnet() { diff --git a/src/settings.h b/src/settings.h index ccc6358..b771293 100644 --- a/src/settings.h +++ b/src/settings.h @@ -16,14 +16,8 @@ public: static Settings* init(); static Settings* getInstance(); - QString getUsernamePassword(); - QString getHost(); - QString getPort(); - - bool loadFromSettings(); - bool loadFromFile(); - - void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password); + Config getSettings(); + void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password); bool isTestnet(); void setTestnet(bool isTestnet); @@ -43,11 +37,13 @@ public: bool isSaplingActive(); - const QString& getZcashdConfLocation() { return confLocation; } + void setUsingZcashConf(QString confLocation); + const QString& getZcashdConfLocation() { return _confLocation; } void setZECPrice(double p) { zecPrice = p; } double getZECPrice(); + QString getUSDFormat (double bal); QString getZECDisplayFormat (double bal); QString getZECUSDDisplayFormat(double bal); @@ -59,17 +55,10 @@ private: static Settings* instance; - Config* currentConfig; - - Config* defaults = nullptr; - Config* zcashconf = nullptr; - Config* uisettings = nullptr; - - QString confLocation; - - bool _isTestnet = false; - bool _isSyncing = false; - int _blockNumber = 0; + QString _confLocation; + bool _isTestnet = false; + bool _isSyncing = false; + int _blockNumber = 0; double zecPrice = 0.0; }; diff --git a/src/settings.ui b/src/settings.ui index 6c2fe13..19a5050 100644 --- a/src/settings.ui +++ b/src/settings.ui @@ -20,7 +20,7 @@ - 1 + 0 @@ -60,7 +60,7 @@ - 127.0.0.1 + @@ -80,7 +80,7 @@ - 8232 +