Refactor RPC
This commit is contained in:
243
src/connection.cpp
Normal file
243
src/connection.cpp
Normal file
@@ -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
Normal file
64
src/connection.h
Normal file
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
|
||||
void restoreSavedStates();
|
||||
|
||||
RPC* rpc;
|
||||
RPC* rpc = nullptr;
|
||||
|
||||
QMovie* loadingMovie;
|
||||
};
|
||||
|
||||
@@ -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
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
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;
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user