Browse Source

Refactor RPC

recurring
Aditya Kulkarni 6 years ago
parent
commit
08bf060186
  1. 243
      src/connection.cpp
  2. 64
      src/connection.h
  3. 29
      src/connection.ui
  4. 3
      src/main.cpp
  5. 26
      src/mainwindow.cpp
  6. 2
      src/mainwindow.h
  7. 4
      src/precompiled.h
  8. 30
      src/rpc.cpp
  9. 10
      src/rpc.h
  10. 6
      zec-qt-wallet.pro

243
src/connection.cpp

@ -0,0 +1,243 @@
#include "connection.h"
#include "mainwindow.h"
#include "settings.h"
#include "ui_connection.h"
#include "rpc.h"
#include "precompiled.h"
using json = nlohmann::json;
class LoadingDialog : public QDialog {
//Q_OBJECT
public:
LoadingDialog(QWidget* parent);
~LoadingDialog();
public slots:
void reject();
};
LoadingDialog::LoadingDialog(QWidget* parent) : QDialog(parent) {}
LoadingDialog::~LoadingDialog() {}
void LoadingDialog::reject() {
//event->ignore();
}
ConnectionLoader::ConnectionLoader(MainWindow* main) {
this->main = main;
d = new LoadingDialog(main);
connD = new Ui_ConnectionDialog();
connD->setupUi(d);
// Center on screen
QRect screenGeometry = QApplication::desktop()->screenGeometry(d);
int x = (screenGeometry.width() - d->width()) / 2;
int y = (screenGeometry.height() - d->height()) / 2;
d->move(x, y);
connD->buttonBox->setEnabled(false);
d->show();
}
ConnectionLoader::~ConnectionLoader() {
delete d;
delete connD;
}
void ConnectionLoader::getConnection(std::function<void(RPC*)> cb) {
// Priority 1: Try to connect to detect zcash.conf and connect to it.
bool isZcashConfPresent = false;
auto conn = autoDetectZcashConf();
// If not autodetected, go and read the UI Settings
if (conn.get() != nullptr) {
isZcashConfPresent = true;
} else {
conn = loadFromSettings();
if (conn.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);
connD->buttonBox->setEnabled(true);
cb(nullptr);
}
}
QNetworkAccessManager* client = new QNetworkAccessManager(main);
QUrl myurl;
myurl.setScheme("http");
myurl.setHost(Settings::getInstance()->getHost());
myurl.setPort(Settings::getInstance()->getPort().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());
auto connection = new Connection(client, request);
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
{"method", "getinfo"}
};
connection->doRPC(payload,
[=] (auto result) {
// Success
d->close();
cb(new RPC(connection, main));
},
[=] (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);
}
);
}
/**
* Try to automatically detect a zcash.conf file in the correct location and load parameters
*/
std::shared_ptr<ConnectionConfig*> ConnectionLoader::autoDetectZcashConf() {
#ifdef Q_OS_LINUX
auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".zcash/zcash.conf");
#elif defined(Q_OS_DARWIN)
auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "/Library/Application Support/Zcash/zcash.conf");
#else
auto confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Zcash/zcash.conf");
#endif
confLocation = QDir::cleanPath(confLocation);
if (confLocation.isNull()) {
// No zcash file, just return with nothing
return nullptr;
}
QFile file(confLocation);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << file.errorString();
return nullptr;
}
QTextStream in(&file);
auto zcashconf = new ConnectionConfig();
zcashconf->host = "127.0.0.1";
zcashconf->connType = ConnectionType::DetectedConfExternalZcashD;
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 = "8232";
file.close();
return std::make_shared<ConnectionConfig*>(zcashconf);
}
/**
* Load connection settings from the UI, which indicates an unknown, external zcashd
*/
std::shared_ptr<ConnectionConfig*> ConnectionLoader::loadFromSettings() {
// 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();
if (username.isEmpty() || password.isEmpty())
return nullptr;
auto uiConfig = new ConnectionConfig{ host, port, username, password, ConnectionType::UISettingsZCashD };
return std::make_shared<ConnectionConfig*>(uiConfig);
}
Connection::Connection(QNetworkAccessManager* c, QNetworkRequest* r) {
this->restclient = c;
this->request = r;
}
Connection::~Connection() {
delete restclient;
delete request;
}
void Connection::doRPC(const json& payload, const std::function<void(json)>& cb,
const std::function<void(QNetworkReply::NetworkError, const json&)>& ne) {
QNetworkReply *reply = restclient->post(*request, QByteArray::fromStdString(payload.dump()));
QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
auto parsed = json::parse(reply->readAll(), nullptr, false);
ne(reply->error(), parsed);
return;
}
auto parsed = json::parse(reply->readAll(), nullptr, false);
if (parsed.is_discarded()) {
ne(reply->error(), "Unknown error");
}
cb(parsed["result"]);
});
}

64
src/connection.h

@ -0,0 +1,64 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include "ui_connection.h"
#include "precompiled.h"
using json = nlohmann::json;
class MainWindow;
class RPC;
enum ConnectionType {
DetectedConfExternalZcashD = 1,
UISettingsZCashD,
InternalZcashD
};
struct ConnectionConfig {
QString host;
QString port;
QString rpcuser;
QString rpcpassword;
ConnectionType connType;
};
class LoadingDialog;
class ConnectionLoader {
public:
ConnectionLoader(MainWindow* main);
~ConnectionLoader();
void getConnection(std::function<void(RPC*)> cb);
private:
std::shared_ptr<ConnectionConfig*> autoDetectZcashConf();
std::shared_ptr<ConnectionConfig*> loadFromSettings();
LoadingDialog* d;
Ui_ConnectionDialog* connD;
MainWindow* main;
};
/**
* Represents a connection to a zcashd. It may even start a new zcashd if needed.
* This is also a UI class, so it may show a dialog waiting for the connection.
*/
class Connection {
public:
Connection(QNetworkAccessManager* c, QNetworkRequest* r);
~Connection();
QNetworkAccessManager* restclient;
QNetworkRequest* request;
void doRPC(const json& payload, const std::function<void(json)>& cb,
const std::function<void(QNetworkReply::NetworkError, const json&)>& ne);
};
#endif

29
src/connection.ui

@ -11,10 +11,13 @@
</rect>
</property>
<property name="windowTitle">
<string>Connecting to zcashd</string>
<string>zec-qt-wallet</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<item row="2" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -24,17 +27,27 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
<item row="0" column="1">
<widget class="QLabel" name="status">
<property name="text">
<string>Connection Status</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="status">
<widget class="QLabel" name="icon">
<property name="text">
<string>Connection Status</string>
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>

3
src/main.cpp

@ -19,12 +19,11 @@ int main(int argc, char *argv[])
#endif
std::srand(std::time(nullptr));
Settings::init();
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
QCoreApplication::setApplicationName("zec-qt-wallet");
Settings::init();
MainWindow w;
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
w.show();

26
src/mainwindow.cpp

@ -11,6 +11,7 @@
#include "utils.h"
#include "turnstile.h"
#include "senttxstore.h"
#include "connection.h"
#include "precompiled.h"
@ -62,13 +63,17 @@ MainWindow::MainWindow(QWidget *parent) :
setupBalancesTab();
setupTurnstileDialog();
rpc = new RPC(new QNetworkAccessManager(this), this);
rpc->refreshZECPrice();
rpc->refresh(true); // Force refresh first time
restoreSavedStates();
new ConnectionLoader(this).getConnection([=] (RPC* rpc) {
if (rpc == nullptr)
return;
this->rpc = rpc;
this->rpc->refreshZECPrice();
this->rpc->refresh(true); // Force refresh first time
});
}
void MainWindow::restoreSavedStates() {
QSettings s;
@ -385,11 +390,14 @@ void MainWindow::setupSettingsModal() {
settings.rpcuser->text(),
settings.rpcpassword->text());
this->rpc->reloadConnectionInfo();
auto me = this;
ConnectionLoader(this).getConnection([&me] (auto newrpc) {
delete me->rpc;
me->rpc = newrpc;
// Then refresh everything.
me->rpc->refresh(true);
});
}
// Then refresh everything.
this->rpc->refresh(true);
};
});

2
src/mainwindow.h

@ -86,7 +86,7 @@ private:
void restoreSavedStates();
RPC* rpc;
RPC* rpc = nullptr;
QMovie* loadingMovie;
};

4
src/precompiled.h

@ -7,7 +7,6 @@
#include <ctime>
#include <cmath>
#include <QApplication>
#include <QFontDatabase>
#include <QAbstractTableModel>
#include <QClipboard>
@ -23,6 +22,7 @@
#include <QDateTime>
#include <QTimer>
#include <QSettings>
#include <QStyle>
#include <QFile>
#include <QErrorMessage>
#include <QApplication>
@ -44,6 +44,8 @@
#include <QAbstractTableModel>
#include <QAbstractItemModel>
#include <QObject>
#include <QApplication>
#include <QDesktopWidget>
#include "3rdparty/json/json.hpp"
#include "3rdparty/qrcode/QrCode.hpp"

30
src/rpc.cpp

@ -6,8 +6,8 @@
using json = nlohmann::json;
RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
this->restclient = client;
RPC::RPC(Connection* conn, MainWindow* main) {
this->conn = conn;
this->main = main;
this->ui = main->ui;
@ -26,8 +26,6 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
main->ui->transactionsTable->setColumnWidth(2, 200);
main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
reloadConnectionInfo();
// Set up timer to refresh Price
priceTimer = new QTimer(main);
QObject::connect(priceTimer, &QTimer::timeout, [=]() {
@ -49,7 +47,6 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
});
// Start at every 10s. When an operation is pending, this will change to every second
txTimer->start(Utils::updateSpeed);
}
RPC::~RPC() {
@ -64,27 +61,12 @@ RPC::~RPC() {
delete allBalances;
delete zaddresses;
delete restclient;
delete conn;
}
void RPC::reloadConnectionInfo() {
// Reset for any errors caused.
firstTime = true;
QUrl myurl;
myurl.setScheme("http"); //https also applicable
myurl.setHost(Settings::getInstance()->getHost());
myurl.setPort(Settings::getInstance()->getPort().toInt());
request.setUrl(myurl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
QString headerData = "Basic " + Settings::getInstance()->getUsernamePassword().toLocal8Bit().toBase64();
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
void RPC::doRPC(const json& payload, const std::function<void(json)>& cb) {
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
QNetworkReply *reply = conn->restclient->post(*conn->request, QByteArray::fromStdString(payload.dump()));
QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater();
@ -230,7 +212,7 @@ void RPC::getTransactions(const std::function<void(json)>& cb) {
}
void RPC::doSendRPC(const json& payload, const std::function<void(json)>& cb, const std::function<void(QString)>& err) {
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
QNetworkReply *reply = conn->restclient->post(*conn->request, QByteArray::fromStdString(payload.dump()));
QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater();
@ -790,7 +772,7 @@ void RPC::refreshZECPrice() {
QNetworkRequest req;
req.setUrl(cmcURL);
QNetworkReply *reply = restclient->get(req);
QNetworkReply *reply = conn->restclient->get(req);
QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater();

10
src/rpc.h

@ -8,6 +8,7 @@
#include "txtablemodel.h"
#include "ui_mainwindow.h"
#include "mainwindow.h"
#include "connection.h"
using json = nlohmann::json;
@ -27,7 +28,7 @@ struct TransactionItem {
class RPC
{
public:
RPC(QNetworkAccessManager* restclient, MainWindow* main);
RPC(Connection* conn, MainWindow* main);
~RPC();
void refresh(bool force = false);
@ -45,8 +46,6 @@ public:
const QList<UnspentOutput>* getUTXOs() { return utxos; }
const QMap<QString, double>* getAllBalances() { return allBalances; }
void reloadConnectionInfo();
void newZaddr(bool sapling, const std::function<void(json)>& cb);
void newTaddr(const std::function<void(json)>& cb);
@ -69,7 +68,7 @@ public:
for (auto item: payloads) {
json payload = payloadGenerator(item);
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
QNetworkReply *reply = conn->restclient->post(*conn->request, QByteArray::fromStdString(payload.dump()));
QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater();
@ -133,8 +132,7 @@ private:
void handleConnectionError (const QString& error);
void handleTxError (const QString& error);
QNetworkAccessManager* restclient;
QNetworkRequest request;
Connection* conn = nullptr;
QList<UnspentOutput>* utxos = nullptr;
QMap<QString, double>* allBalances = nullptr;

6
zec-qt-wallet.pro

@ -53,7 +53,8 @@ SOURCES += \
src/txtablemodel.cpp \
src/turnstile.cpp \
src/utils.cpp \
src/qrcodelabel.cpp
src/qrcodelabel.cpp \
src/connection.cpp
HEADERS += \
src/mainwindow.h \
@ -70,7 +71,8 @@ HEADERS += \
src/senttxstore.h \
src/turnstile.h \
src/utils.h \
src/qrcodelabel.h
src/qrcodelabel.h \
src/connection.h
FORMS += \
src/mainwindow.ui \

Loading…
Cancel
Save