Merge pull request #22 from adityapk00/connection
Separate out RPC to prep for embedded zcashd
This commit is contained in:
319
src/connection.cpp
Normal file
319
src/connection.cpp
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
#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, RPC* rpc) {
|
||||||
|
this->main = main;
|
||||||
|
this->rpc = rpc;
|
||||||
|
|
||||||
|
d = new QDialog(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::loadConnection() {
|
||||||
|
// Priority 1: Try to connect to detect zcash.conf and connect to it.
|
||||||
|
bool isZcashConfPresent = false;
|
||||||
|
auto config = autoDetectZcashConf();
|
||||||
|
|
||||||
|
// If not autodetected, go and read the UI Settings
|
||||||
|
if (config.get() != nullptr) {
|
||||||
|
isZcashConfPresent = true;
|
||||||
|
} else {
|
||||||
|
config = loadFromSettings();
|
||||||
|
|
||||||
|
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.";
|
||||||
|
|
||||||
|
showError(explanation);
|
||||||
|
rpc->setConnection(nullptr);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto connection = makeConnection(config);
|
||||||
|
refreshZcashdState(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection* ConnectionLoader::makeConnection(std::shared_ptr<ConnectionConfig> 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();
|
||||||
|
request->setUrl(myurl);
|
||||||
|
request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
|
||||||
|
|
||||||
|
QString userpass = config.get()->rpcuser % ":" % config.get()->rpcpassword;
|
||||||
|
QString headerData = "Basic " + userpass.toLocal8Bit().toBase64();
|
||||||
|
request->setRawHeader("Authorization", headerData.toLocal8Bit());
|
||||||
|
|
||||||
|
return new Connection(main, client, request, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionLoader::refreshZcashdState(Connection* connection) {
|
||||||
|
json payload = {
|
||||||
|
{"jsonrpc", "1.0"},
|
||||||
|
{"id", "someid"},
|
||||||
|
{"method", "getinfo"}
|
||||||
|
};
|
||||||
|
connection->doRPC(payload,
|
||||||
|
[=] (auto) {
|
||||||
|
// Success
|
||||||
|
d->hide();
|
||||||
|
rpc->setConnection(connection);
|
||||||
|
},
|
||||||
|
[=] (auto reply, auto res) {
|
||||||
|
auto err = reply->error();
|
||||||
|
// 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<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;
|
||||||
|
zcashconf->usingZcashConf = true;
|
||||||
|
|
||||||
|
Settings::getInstance()->setUsingZcashConf(confLocation);
|
||||||
|
|
||||||
|
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::shared_ptr<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, false, ConnectionType::UISettingsZCashD };
|
||||||
|
|
||||||
|
return std::shared_ptr<ConnectionConfig>(uiConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Connection::Connection(MainWindow* m, QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr<ConnectionConfig> conf) {
|
||||||
|
this->restclient = c;
|
||||||
|
this->request = r;
|
||||||
|
this->config = conf;
|
||||||
|
this->main = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection::~Connection() {
|
||||||
|
delete restclient;
|
||||||
|
delete request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Connection::doRPC(const json& payload, const std::function<void(json)>& cb,
|
||||||
|
const std::function<void(QNetworkReply*, 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, parsed);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parsed = json::parse(reply->readAll(), nullptr, false);
|
||||||
|
if (parsed.is_discarded()) {
|
||||||
|
ne(reply, "Unknown error");
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(parsed["result"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::doRPCWithDefaultErrorHandling(const json& payload, const std::function<void(json)>& cb) {
|
||||||
|
doRPC(payload, cb, [=] (auto reply, auto parsed) {
|
||||||
|
if (!parsed.is_discarded() && !parsed["error"]["message"].is_null()) {
|
||||||
|
showTxError(QString::fromStdString(parsed["error"]["message"]));
|
||||||
|
} else {
|
||||||
|
showTxError(reply->errorString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::doRPCIgnoreError(const json& payload, const std::function<void(json)>& cb) {
|
||||||
|
doRPC(payload, cb, [=] (auto, auto) {
|
||||||
|
// Ignored error handling
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::showTxError(const QString& error) {
|
||||||
|
if (error.isNull()) return;
|
||||||
|
|
||||||
|
QMessageBox msg(main);
|
||||||
|
msg.setIcon(QMessageBox::Icon::Critical);
|
||||||
|
msg.setWindowTitle("Transaction Error");
|
||||||
|
|
||||||
|
msg.setText("There was an error sending the transaction. The error was: \n\n"
|
||||||
|
+ error);
|
||||||
|
|
||||||
|
msg.exec();
|
||||||
|
}
|
||||||
125
src/connection.h
Normal file
125
src/connection.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#ifndef CONNECTION_H
|
||||||
|
#define CONNECTION_H
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_connection.h"
|
||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
class RPC;
|
||||||
|
|
||||||
|
enum ConnectionType {
|
||||||
|
DetectedConfExternalZcashD = 1,
|
||||||
|
UISettingsZCashD,
|
||||||
|
InternalZcashD
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConnectionConfig {
|
||||||
|
QString host;
|
||||||
|
QString port;
|
||||||
|
QString rpcuser;
|
||||||
|
QString rpcpassword;
|
||||||
|
bool usingZcashConf;
|
||||||
|
|
||||||
|
ConnectionType connType;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
|
||||||
|
class ConnectionLoader {
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConnectionLoader(MainWindow* main, RPC* rpc);
|
||||||
|
~ConnectionLoader();
|
||||||
|
|
||||||
|
void loadConnection();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<ConnectionConfig> autoDetectZcashConf();
|
||||||
|
std::shared_ptr<ConnectionConfig> loadFromSettings();
|
||||||
|
|
||||||
|
Connection* makeConnection(std::shared_ptr<ConnectionConfig> config);
|
||||||
|
|
||||||
|
void refreshZcashdState(Connection* connection);
|
||||||
|
int getProgressFromStatus(QString status);
|
||||||
|
|
||||||
|
void showError(QString explanation);
|
||||||
|
|
||||||
|
QDialog* d;
|
||||||
|
Ui_ConnectionDialog* connD;
|
||||||
|
|
||||||
|
MainWindow* main;
|
||||||
|
RPC* rpc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(MainWindow* m, QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr<ConnectionConfig> conf);
|
||||||
|
~Connection();
|
||||||
|
|
||||||
|
QNetworkAccessManager* restclient;
|
||||||
|
QNetworkRequest* request;
|
||||||
|
std::shared_ptr<ConnectionConfig> config;
|
||||||
|
MainWindow* main;
|
||||||
|
|
||||||
|
void doRPC(const json& payload, const std::function<void(json)>& cb,
|
||||||
|
const std::function<void(QNetworkReply*, const json&)>& ne);
|
||||||
|
void doRPCWithDefaultErrorHandling(const json& payload, const std::function<void(json)>& cb);
|
||||||
|
void doRPCIgnoreError(const json& payload, const std::function<void(json)>& cb) ;
|
||||||
|
|
||||||
|
void showTxError(const QString& error);
|
||||||
|
|
||||||
|
// Batch method. Note: Because of the template, it has to be in the header file.
|
||||||
|
template<class T>
|
||||||
|
void doBatchRPC(const QList<T>& payloads,
|
||||||
|
std::function<json(T)> payloadGenerator,
|
||||||
|
std::function<void(QMap<T, json>*)> cb) {
|
||||||
|
auto responses = new QMap<T, json>(); // zAddr -> list of responses for each call.
|
||||||
|
int totalSize = payloads.size();
|
||||||
|
|
||||||
|
for (auto item: payloads) {
|
||||||
|
json payload = payloadGenerator(item);
|
||||||
|
|
||||||
|
QNetworkReply *reply = restclient->post(*request, QByteArray::fromStdString(payload.dump()));
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
auto all = reply->readAll();
|
||||||
|
auto parsed = json::parse(all.toStdString(), nullptr, false);
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qDebug() << QString::fromStdString(parsed.dump());
|
||||||
|
qDebug() << reply->errorString();
|
||||||
|
|
||||||
|
(*responses)[item] = json::object(); // Empty object
|
||||||
|
} else {
|
||||||
|
if (parsed.is_discarded()) {
|
||||||
|
(*responses)[item] = json::object(); // Empty object
|
||||||
|
} else {
|
||||||
|
(*responses)[item] = parsed["result"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waitTimer = new QTimer(main);
|
||||||
|
QObject::connect(waitTimer, &QTimer::timeout, [=]() {
|
||||||
|
if (responses->size() == totalSize) {
|
||||||
|
waitTimer->stop();
|
||||||
|
|
||||||
|
cb(responses);
|
||||||
|
|
||||||
|
waitTimer->deleteLater();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waitTimer->start(100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,37 +7,79 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>513</width>
|
<width>513</width>
|
||||||
<height>286</height>
|
<height>201</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Connecting to zcashd</string>
|
<string>zec-qt-wallet</string>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="2" column="0">
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="title">
|
||||||
|
<property name="text">
|
||||||
|
<string>Your zcashd is still loading. Please wait...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::Close</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QProgressBar" name="progressBar">
|
|
||||||
<property name="value">
|
|
||||||
<number>24</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="status">
|
<widget class="QLabel" name="status">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Connection Status</string>
|
<string>Connection Status</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0" rowspan="2">
|
||||||
|
<widget class="QLabel" name="icon">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|||||||
@@ -19,12 +19,11 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::srand(std::time(nullptr));
|
std::srand(std::time(nullptr));
|
||||||
|
Settings::init();
|
||||||
|
|
||||||
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
|
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
|
||||||
QCoreApplication::setApplicationName("zec-qt-wallet");
|
QCoreApplication::setApplicationName("zec-qt-wallet");
|
||||||
|
|
||||||
Settings::init();
|
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
|
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
|
||||||
w.show();
|
w.show();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "turnstile.h"
|
#include "turnstile.h"
|
||||||
#include "senttxstore.h"
|
#include "senttxstore.h"
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
@@ -62,14 +63,12 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
setupBalancesTab();
|
setupBalancesTab();
|
||||||
setupTurnstileDialog();
|
setupTurnstileDialog();
|
||||||
|
|
||||||
rpc = new RPC(new QNetworkAccessManager(this), this);
|
rpc = new RPC(this);
|
||||||
rpc->refreshZECPrice();
|
|
||||||
|
|
||||||
rpc->refresh(true); // Force refresh first time
|
|
||||||
|
|
||||||
restoreSavedStates();
|
restoreSavedStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::restoreSavedStates() {
|
void MainWindow::restoreSavedStates() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
restoreGeometry(s.value("geometry").toByteArray());
|
restoreGeometry(s.value("geometry").toByteArray());
|
||||||
@@ -353,10 +352,11 @@ void MainWindow::setupSettingsModal() {
|
|||||||
settings.port->setValidator(&validator);
|
settings.port->setValidator(&validator);
|
||||||
|
|
||||||
// Load current values into the dialog
|
// Load current values into the dialog
|
||||||
settings.hostname->setText(Settings::getInstance()->getHost());
|
auto conf = Settings::getInstance()->getSettings();
|
||||||
settings.port->setText(Settings::getInstance()->getPort());
|
settings.hostname->setText(conf.host);
|
||||||
settings.rpcuser->setText(Settings::getInstance()->getUsernamePassword().split(":")[0]);
|
settings.port->setText(conf.port);
|
||||||
settings.rpcpassword->setText(Settings::getInstance()->getUsernamePassword().split(":")[1]);
|
settings.rpcuser->setText(conf.rpcuser);
|
||||||
|
settings.rpcpassword->setText(conf.rpcpassword);
|
||||||
|
|
||||||
// If values are coming from zcash.conf, then disable all the fields
|
// If values are coming from zcash.conf, then disable all the fields
|
||||||
auto zcashConfLocation = Settings::getInstance()->getZcashdConfLocation();
|
auto zcashConfLocation = Settings::getInstance()->getZcashdConfLocation();
|
||||||
@@ -387,11 +387,9 @@ void MainWindow::setupSettingsModal() {
|
|||||||
settings.rpcuser->text(),
|
settings.rpcuser->text(),
|
||||||
settings.rpcpassword->text());
|
settings.rpcpassword->text());
|
||||||
|
|
||||||
this->rpc->reloadConnectionInfo();
|
auto cl = new ConnectionLoader(this, rpc);
|
||||||
|
cl->loadConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then refresh everything.
|
|
||||||
this->rpc->refresh(true);
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ private:
|
|||||||
|
|
||||||
void restoreSavedStates();
|
void restoreSavedStates();
|
||||||
|
|
||||||
RPC* rpc;
|
RPC* rpc = nullptr;
|
||||||
|
|
||||||
QMovie* loadingMovie;
|
QMovie* loadingMovie;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
@@ -23,6 +22,7 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QStyle>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QErrorMessage>
|
#include <QErrorMessage>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -44,6 +44,8 @@
|
|||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
#include "3rdparty/json/json.hpp"
|
||||||
#include "3rdparty/qrcode/QrCode.hpp"
|
#include "3rdparty/qrcode/QrCode.hpp"
|
||||||
|
|||||||
225
src/rpc.cpp
225
src/rpc.cpp
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
RPC::RPC(MainWindow* main) {
|
||||||
this->restclient = client;
|
auto cl = new ConnectionLoader(main, this);
|
||||||
|
cl->loadConnection();
|
||||||
|
|
||||||
this->main = main;
|
this->main = main;
|
||||||
this->ui = main->ui;
|
this->ui = main->ui;
|
||||||
|
|
||||||
@@ -16,17 +18,11 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
|||||||
// Setup balances table model
|
// Setup balances table model
|
||||||
balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
|
balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
|
||||||
main->ui->balancesTable->setModel(balancesTableModel);
|
main->ui->balancesTable->setModel(balancesTableModel);
|
||||||
main->ui->balancesTable->setColumnWidth(0, 300);
|
|
||||||
|
|
||||||
// Setup transactions table model
|
// Setup transactions table model
|
||||||
transactionsTableModel = new TxTableModel(ui->transactionsTable);
|
transactionsTableModel = new TxTableModel(ui->transactionsTable);
|
||||||
main->ui->transactionsTable->setModel(transactionsTableModel);
|
main->ui->transactionsTable->setModel(transactionsTableModel);
|
||||||
main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
|
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);
|
|
||||||
|
|
||||||
reloadConnectionInfo();
|
|
||||||
|
|
||||||
// Set up timer to refresh Price
|
// Set up timer to refresh Price
|
||||||
priceTimer = new QTimer(main);
|
priceTimer = new QTimer(main);
|
||||||
@@ -49,7 +45,6 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
|||||||
});
|
});
|
||||||
// Start at every 10s. When an operation is pending, this will change to every second
|
// Start at every 10s. When an operation is pending, this will change to every second
|
||||||
txTimer->start(Utils::updateSpeed);
|
txTimer->start(Utils::updateSpeed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RPC::~RPC() {
|
RPC::~RPC() {
|
||||||
@@ -64,49 +59,16 @@ RPC::~RPC() {
|
|||||||
delete allBalances;
|
delete allBalances;
|
||||||
delete zaddresses;
|
delete zaddresses;
|
||||||
|
|
||||||
delete restclient;
|
delete conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::reloadConnectionInfo() {
|
void RPC::setConnection(Connection* c) {
|
||||||
// Reset for any errors caused.
|
if (c == nullptr) return;
|
||||||
firstTime = true;
|
|
||||||
|
|
||||||
QUrl myurl;
|
delete conn;
|
||||||
myurl.setScheme("http"); //https also applicable
|
this->conn = c;
|
||||||
myurl.setHost(Settings::getInstance()->getHost());
|
|
||||||
myurl.setPort(Settings::getInstance()->getPort().toInt());
|
|
||||||
|
|
||||||
request.setUrl(myurl);
|
refresh();
|
||||||
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()));
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
auto parsed = json::parse(reply->readAll(), nullptr, false);
|
|
||||||
if (!parsed.is_discarded() && !parsed["error"]["message"].is_null()) {
|
|
||||||
handleConnectionError(QString::fromStdString(parsed["error"]["message"]));
|
|
||||||
} else {
|
|
||||||
handleConnectionError(reply->errorString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto parsed = json::parse(reply->readAll(), nullptr, false);
|
|
||||||
if (parsed.is_discarded()) {
|
|
||||||
handleConnectionError("Unknown error");
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(parsed["result"]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::getZAddresses(const std::function<void(json)>& cb) {
|
void RPC::getZAddresses(const std::function<void(json)>& cb) {
|
||||||
@@ -116,7 +78,7 @@ void RPC::getZAddresses(const std::function<void(json)>& cb) {
|
|||||||
{"method", "z_listaddresses"},
|
{"method", "z_listaddresses"},
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::getTransparentUnspent(const std::function<void(json)>& cb) {
|
void RPC::getTransparentUnspent(const std::function<void(json)>& cb) {
|
||||||
@@ -127,7 +89,7 @@ void RPC::getTransparentUnspent(const std::function<void(json)>& cb) {
|
|||||||
{"params", {0}} // Get UTXOs with 0 confirmations as well.
|
{"params", {0}} // Get UTXOs with 0 confirmations as well.
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::getZUnspent(const std::function<void(json)>& cb) {
|
void RPC::getZUnspent(const std::function<void(json)>& cb) {
|
||||||
@@ -138,7 +100,7 @@ void RPC::getZUnspent(const std::function<void(json)>& cb) {
|
|||||||
{"params", {0}} // Get UTXOs with 0 confirmations as well.
|
{"params", {0}} // Get UTXOs with 0 confirmations as well.
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::newZaddr(bool sapling, const std::function<void(json)>& cb) {
|
void RPC::newZaddr(bool sapling, const std::function<void(json)>& cb) {
|
||||||
@@ -149,7 +111,7 @@ void RPC::newZaddr(bool sapling, const std::function<void(json)>& cb) {
|
|||||||
{"params", { sapling ? "sapling" : "sprout" }},
|
{"params", { sapling ? "sapling" : "sprout" }},
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::newTaddr(const std::function<void(json)>& cb) {
|
void RPC::newTaddr(const std::function<void(json)>& cb) {
|
||||||
@@ -159,7 +121,7 @@ void RPC::newTaddr(const std::function<void(json)>& cb) {
|
|||||||
{"method", "getnewaddress"},
|
{"method", "getnewaddress"},
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::getZPrivKey(QString addr, const std::function<void(json)>& cb) {
|
void RPC::getZPrivKey(QString addr, const std::function<void(json)>& cb) {
|
||||||
@@ -170,7 +132,7 @@ void RPC::getZPrivKey(QString addr, const std::function<void(json)>& cb) {
|
|||||||
{"params", { addr.toStdString() }},
|
{"params", { addr.toStdString() }},
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::getTPrivKey(QString addr, const std::function<void(json)>& cb) {
|
void RPC::getTPrivKey(QString addr, const std::function<void(json)>& cb) {
|
||||||
@@ -181,7 +143,7 @@ void RPC::getTPrivKey(QString addr, const std::function<void(json)>& cb) {
|
|||||||
{"params", { addr.toStdString() }},
|
{"params", { addr.toStdString() }},
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::importZPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb) {
|
void RPC::importZPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb) {
|
||||||
@@ -192,7 +154,7 @@ void RPC::importZPrivKey(QString addr, bool rescan, const std::function<void(jso
|
|||||||
{"params", { addr.toStdString(), (rescan? "yes" : "no") }},
|
{"params", { addr.toStdString(), (rescan? "yes" : "no") }},
|
||||||
};
|
};
|
||||||
|
|
||||||
doSendRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -204,7 +166,7 @@ void RPC::importTPrivKey(QString addr, bool rescan, const std::function<void(jso
|
|||||||
{"params", { addr.toStdString(), (rescan? "yes" : "no") }},
|
{"params", { addr.toStdString(), (rescan? "yes" : "no") }},
|
||||||
};
|
};
|
||||||
|
|
||||||
doSendRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -216,7 +178,7 @@ void RPC::getBalance(const std::function<void(json)>& cb) {
|
|||||||
{"params", {0}} // Get Unconfirmed balance as well.
|
{"params", {0}} // Get Unconfirmed balance as well.
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::getTransactions(const std::function<void(json)>& cb) {
|
void RPC::getTransactions(const std::function<void(json)>& cb) {
|
||||||
@@ -226,39 +188,7 @@ void RPC::getTransactions(const std::function<void(json)>& cb) {
|
|||||||
{"method", "listtransactions"}
|
{"method", "listtransactions"}
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, 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()));
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
auto parsed = json::parse(reply->readAll(), nullptr, false);
|
|
||||||
if (!parsed.is_discarded() && !parsed["error"]["message"].is_null()) {
|
|
||||||
err(QString::fromStdString(parsed["error"]["message"]));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
err(reply->errorString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto parsed = json::parse(reply->readAll(), nullptr, false);
|
|
||||||
if (parsed.is_discarded()) {
|
|
||||||
err("Unknown error");
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(parsed["result"]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default implementation of a Send RPC that default shows an error message box with the error.
|
|
||||||
void RPC::doSendRPC(const json& payload, const std::function<void(json)>& cb) {
|
|
||||||
doSendRPC(payload, cb, [=](auto error) { this->handleTxError(error); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
|
void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
|
||||||
@@ -269,76 +199,9 @@ void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
|
|||||||
{"params", params}
|
{"params", params}
|
||||||
};
|
};
|
||||||
|
|
||||||
doSendRPC(payload, cb);
|
conn->doRPCWithDefaultErrorHandling(payload, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPC::handleConnectionError(const QString& error) {
|
|
||||||
if (error.isNull()) return;
|
|
||||||
|
|
||||||
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
|
|
||||||
main->statusIcon->setPixmap(icon.pixmap(16, 16));
|
|
||||||
main->statusLabel->setText("No Connection");
|
|
||||||
|
|
||||||
if (firstTime) {
|
|
||||||
this->firstTime = false;
|
|
||||||
|
|
||||||
QMessageBox msg(main);
|
|
||||||
msg.setIcon(QMessageBox::Icon::Critical);
|
|
||||||
msg.setWindowTitle("Connection Error");
|
|
||||||
|
|
||||||
QString explanation;
|
|
||||||
if (error.contains("authentication", Qt::CaseInsensitive)) {
|
|
||||||
explanation = QString()
|
|
||||||
% "\n\nThis is most likely because of misconfigured rpcuser/rpcpassword. "
|
|
||||||
% "zcashd needs the following options set in ~/.zcash/zcash.conf\n\n"
|
|
||||||
% "rpcuser=<someusername>\n"
|
|
||||||
% "rpcpassword=<somepassword>\n"
|
|
||||||
% "\nIf you're connecting to a remote note, you can change the username/password in the "
|
|
||||||
% "File->Settings menu.";
|
|
||||||
} else if (error.contains("connection refused", Qt::CaseInsensitive)) {
|
|
||||||
auto confLocation = Settings::getInstance()->getZcashdConfLocation();
|
|
||||||
if (confLocation.isEmpty()) {
|
|
||||||
explanation = QString()
|
|
||||||
% "\n\nA zcash.conf was not found on this machine. If you are connecting to a remote/non-standard node "
|
|
||||||
% "please set the host/port and user/password in the File->Settings menu.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
explanation = QString()
|
|
||||||
% "\n\nA zcash.conf was found at\n" % confLocation
|
|
||||||
% "\nbut we can't connect to zcashd. Is rpcuser=<user> and rpcpassword=<pass> set in the zcash.conf file?";
|
|
||||||
}
|
|
||||||
} else if (error.contains("internal server error", Qt::CaseInsensitive) ||
|
|
||||||
error.contains("rewinding", Qt::CaseInsensitive) ||
|
|
||||||
error.contains("loading", Qt::CaseInsensitive)) {
|
|
||||||
explanation = QString()
|
|
||||||
% "\n\nIf you just started zcashd, then it's still loading and you might have to wait a while. If zcashd is ready, then you've run into "
|
|
||||||
% "something unexpected, and might need to file a bug report here: https://github.com/adityapk00/zec-qt-wallet/issues";
|
|
||||||
} else {
|
|
||||||
explanation = QString()
|
|
||||||
% "\n\nThis is most likely an internal error. Something unexpected happened. "
|
|
||||||
% "You might need to file a bug report here: https://github.com/adityapk00/zec-qt-wallet/issues";
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.setText("There was a network connection error. The error was: \n\n"
|
|
||||||
+ error + explanation);
|
|
||||||
|
|
||||||
msg.exec();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RPC::handleTxError(const QString& error) {
|
|
||||||
if (error.isNull()) return;
|
|
||||||
|
|
||||||
QMessageBox msg(main);
|
|
||||||
msg.setIcon(QMessageBox::Icon::Critical);
|
|
||||||
msg.setWindowTitle("Transaction Error");
|
|
||||||
|
|
||||||
msg.setText("There was an error sending the transaction. The error was: \n\n"
|
|
||||||
+ error);
|
|
||||||
|
|
||||||
msg.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Build the RPC JSON Parameters for this tx (with the dev fee included if applicable)
|
// Build the RPC JSON Parameters for this tx (with the dev fee included if applicable)
|
||||||
@@ -367,8 +230,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
|
// Refresh received z txs by calling z_listreceivedbyaddress/gettransaction
|
||||||
void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
// We'll only refresh the received Z txs if settings allows us.
|
// We'll only refresh the received Z txs if settings allows us.
|
||||||
if (!Settings::getInstance()->getSaveZtxs()) {
|
if (!Settings::getInstance()->getSaveZtxs()) {
|
||||||
@@ -383,7 +252,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
// and each z-Addr can have multiple received txs.
|
// and each z-Addr can have multiple received txs.
|
||||||
|
|
||||||
// 1. For each z-Addr, get list of received txs
|
// 1. For each z-Addr, get list of received txs
|
||||||
getBatchRPC<QString>(zaddrs,
|
conn->doBatchRPC<QString>(zaddrs,
|
||||||
[=] (QString zaddr) {
|
[=] (QString zaddr) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -420,7 +289,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. For all txids, go and get the details of that txid.
|
// 2. For all txids, go and get the details of that txid.
|
||||||
getBatchRPC<QString>(txids.toList(),
|
conn->doBatchRPC<QString>(txids.toList(),
|
||||||
[=] (QString txid) {
|
[=] (QString txid) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -477,18 +346,24 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
|
|
||||||
/// This will refresh all the balance data from zcashd
|
/// This will refresh all the balance data from zcashd
|
||||||
void RPC::refresh(bool force) {
|
void RPC::refresh(bool force) {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
getInfoThenRefresh(force);
|
getInfoThenRefresh(force);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RPC::getInfoThenRefresh(bool force) {
|
void RPC::getInfoThenRefresh(bool force) {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
{"id", "someid"},
|
{"id", "someid"},
|
||||||
{"method", "getinfo"}
|
{"method", "getinfo"}
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, [=] (const json& reply) {
|
conn->doRPCIgnoreError(payload, [=] (const json& reply) {
|
||||||
// Testnet?
|
// Testnet?
|
||||||
if (!reply["testnet"].is_null()) {
|
if (!reply["testnet"].is_null()) {
|
||||||
Settings::getInstance()->setTestnet(reply["testnet"].get<json::boolean_t>());
|
Settings::getInstance()->setTestnet(reply["testnet"].get<json::boolean_t>());
|
||||||
@@ -517,7 +392,7 @@ void RPC::getInfoThenRefresh(bool force) {
|
|||||||
{"method", "getblockchaininfo"}
|
{"method", "getblockchaininfo"}
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, [=](const json& reply) {
|
conn->doRPCIgnoreError(payload, [=](const json& reply) {
|
||||||
auto progress = reply["verificationprogress"].get<double>();
|
auto progress = reply["verificationprogress"].get<double>();
|
||||||
bool isSyncing = progress < 0.999; // 99.9%
|
bool isSyncing = progress < 0.999; // 99.9%
|
||||||
int blockNumber = reply["blocks"].get<json::number_unsigned_t>();
|
int blockNumber = reply["blocks"].get<json::number_unsigned_t>();
|
||||||
@@ -544,6 +419,9 @@ void RPC::getInfoThenRefresh(bool force) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RPC::refreshAddresses() {
|
void RPC::refreshAddresses() {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
delete zaddresses;
|
delete zaddresses;
|
||||||
zaddresses = new QList<QString>();
|
zaddresses = new QList<QString>();
|
||||||
|
|
||||||
@@ -609,6 +487,9 @@ bool RPC::processUnspent(const json& reply) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void RPC::refreshBalances() {
|
void RPC::refreshBalances() {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
// 1. Get the Balances
|
// 1. Get the Balances
|
||||||
getBalance([=] (json reply) {
|
getBalance([=] (json reply) {
|
||||||
auto balT = QString::fromStdString(reply["transparent"]).toDouble();
|
auto balT = QString::fromStdString(reply["transparent"]).toDouble();
|
||||||
@@ -644,6 +525,9 @@ void RPC::refreshBalances() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RPC::refreshTransactions() {
|
void RPC::refreshTransactions() {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
getTransactions([=] (json reply) {
|
getTransactions([=] (json reply) {
|
||||||
QList<TransactionItem> txdata;
|
QList<TransactionItem> txdata;
|
||||||
|
|
||||||
@@ -672,6 +556,9 @@ void RPC::refreshTransactions() {
|
|||||||
|
|
||||||
// Read sent Z transactions from the file.
|
// Read sent Z transactions from the file.
|
||||||
void RPC::refreshSentZTrans() {
|
void RPC::refreshSentZTrans() {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
auto sentZTxs = SentTxStore::readSentTxFile();
|
auto sentZTxs = SentTxStore::readSentTxFile();
|
||||||
|
|
||||||
QList<QString> txids;
|
QList<QString> txids;
|
||||||
@@ -681,7 +568,7 @@ void RPC::refreshSentZTrans() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up all the txids to get the confirmation count for them.
|
// Look up all the txids to get the confirmation count for them.
|
||||||
getBatchRPC<QString>(txids,
|
conn->doBatchRPC<QString>(txids,
|
||||||
[=] (QString txid) {
|
[=] (QString txid) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -719,6 +606,9 @@ void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RPC::watchTxStatus() {
|
void RPC::watchTxStatus() {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
// Make an RPC to load pending operation statues
|
// Make an RPC to load pending operation statues
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -726,7 +616,7 @@ void RPC::watchTxStatus() {
|
|||||||
{"method", "z_getoperationstatus"},
|
{"method", "z_getoperationstatus"},
|
||||||
};
|
};
|
||||||
|
|
||||||
doRPC(payload, [=] (const json& reply) {
|
conn->doRPCWithDefaultErrorHandling(payload, [=] (const json& reply) {
|
||||||
// There's an array for each item in the status
|
// There's an array for each item in the status
|
||||||
for (auto& it : reply.get<json::array_t>()) {
|
for (auto& it : reply.get<json::array_t>()) {
|
||||||
// If we were watching this Tx and it's status became "success", then we'll show a status bar alert
|
// If we were watching this Tx and it's status became "success", then we'll show a status bar alert
|
||||||
@@ -785,12 +675,15 @@ void RPC::watchTxStatus() {
|
|||||||
|
|
||||||
// Get the ZEC->USD price from coinmarketcap using their API
|
// Get the ZEC->USD price from coinmarketcap using their API
|
||||||
void RPC::refreshZECPrice() {
|
void RPC::refreshZECPrice() {
|
||||||
|
if (conn == nullptr)
|
||||||
|
return noConnection();
|
||||||
|
|
||||||
QUrl cmcURL("https://api.coinmarketcap.com/v1/ticker/");
|
QUrl cmcURL("https://api.coinmarketcap.com/v1/ticker/");
|
||||||
|
|
||||||
QNetworkRequest req;
|
QNetworkRequest req;
|
||||||
req.setUrl(cmcURL);
|
req.setUrl(cmcURL);
|
||||||
|
|
||||||
QNetworkReply *reply = restclient->get(req);
|
QNetworkReply *reply = conn->restclient->get(req);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|||||||
67
src/rpc.h
67
src/rpc.h
@@ -8,6 +8,7 @@
|
|||||||
#include "txtablemodel.h"
|
#include "txtablemodel.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -27,9 +28,11 @@ struct TransactionItem {
|
|||||||
class RPC
|
class RPC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RPC(QNetworkAccessManager* restclient, MainWindow* main);
|
RPC(MainWindow* main);
|
||||||
~RPC();
|
~RPC();
|
||||||
|
|
||||||
|
void setConnection(Connection* c);
|
||||||
|
|
||||||
void refresh(bool force = false);
|
void refresh(bool force = false);
|
||||||
|
|
||||||
void refreshAddresses();
|
void refreshAddresses();
|
||||||
@@ -45,72 +48,19 @@ public:
|
|||||||
const QList<UnspentOutput>* getUTXOs() { return utxos; }
|
const QList<UnspentOutput>* getUTXOs() { return utxos; }
|
||||||
const QMap<QString, double>* getAllBalances() { return allBalances; }
|
const QMap<QString, double>* getAllBalances() { return allBalances; }
|
||||||
|
|
||||||
void reloadConnectionInfo();
|
|
||||||
|
|
||||||
void newZaddr(bool sapling, const std::function<void(json)>& cb);
|
void newZaddr(bool sapling, const std::function<void(json)>& cb);
|
||||||
void newTaddr(const std::function<void(json)>& cb);
|
void newTaddr(const std::function<void(json)>& cb);
|
||||||
|
|
||||||
|
|
||||||
void getZPrivKey(QString addr, const std::function<void(json)>& cb);
|
void getZPrivKey(QString addr, const std::function<void(json)>& cb);
|
||||||
void getTPrivKey(QString addr, const std::function<void(json)>& cb);
|
void getTPrivKey(QString addr, const std::function<void(json)>& cb);
|
||||||
void importZPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb);
|
void importZPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb);
|
||||||
void importTPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb);
|
void importTPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb);
|
||||||
|
|
||||||
Turnstile* getTurnstile() { return turnstile; }
|
Turnstile* getTurnstile() { return turnstile; }
|
||||||
|
Connection* getConnection() { return conn; }
|
||||||
// Batch method. Note: Because of the template, it has to be in the header file.
|
|
||||||
template<class T>
|
|
||||||
void getBatchRPC(const QList<T>& payloads,
|
|
||||||
std::function<json(T)> payloadGenerator,
|
|
||||||
std::function<void(QMap<T, json>*)> cb) {
|
|
||||||
auto responses = new QMap<T, json>(); // zAddr -> list of responses for each call.
|
|
||||||
int totalSize = payloads.size();
|
|
||||||
|
|
||||||
for (auto item: payloads) {
|
|
||||||
json payload = payloadGenerator(item);
|
|
||||||
|
|
||||||
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
auto all = reply->readAll();
|
|
||||||
auto parsed = json::parse(all.toStdString(), nullptr, false);
|
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
qDebug() << QString::fromStdString(parsed.dump());
|
|
||||||
qDebug() << reply->errorString();
|
|
||||||
|
|
||||||
(*responses)[item] = json::object(); // Empty object
|
|
||||||
} else {
|
|
||||||
if (parsed.is_discarded()) {
|
|
||||||
(*responses)[item] = json::object(); // Empty object
|
|
||||||
} else {
|
|
||||||
(*responses)[item] = parsed["result"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waitTimer = new QTimer(main);
|
|
||||||
QObject::connect(waitTimer, &QTimer::timeout, [=]() {
|
|
||||||
if (responses->size() == totalSize) {
|
|
||||||
waitTimer->stop();
|
|
||||||
|
|
||||||
cb(responses);
|
|
||||||
|
|
||||||
waitTimer->deleteLater();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
waitTimer->start(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doRPC (const json& payload, const std::function<void(json)>& cb);
|
void noConnection();
|
||||||
void doSendRPC(const json& payload, const std::function<void(json)>& cb);
|
|
||||||
void doSendRPC(const json& payload, const std::function<void(json)>& cb, const std::function<void(QString)>& err);
|
|
||||||
|
|
||||||
void refreshBalances();
|
void refreshBalances();
|
||||||
|
|
||||||
@@ -133,8 +83,7 @@ private:
|
|||||||
void handleConnectionError (const QString& error);
|
void handleConnectionError (const QString& error);
|
||||||
void handleTxError (const QString& error);
|
void handleTxError (const QString& error);
|
||||||
|
|
||||||
QNetworkAccessManager* restclient;
|
Connection* conn = nullptr;
|
||||||
QNetworkRequest request;
|
|
||||||
|
|
||||||
QList<UnspentOutput>* utxos = nullptr;
|
QList<UnspentOutput>* utxos = nullptr;
|
||||||
QMap<QString, double>* allBalances = nullptr;
|
QMap<QString, double>* allBalances = nullptr;
|
||||||
|
|||||||
106
src/settings.cpp
106
src/settings.cpp
@@ -6,9 +6,6 @@
|
|||||||
Settings* Settings::instance = nullptr;
|
Settings* Settings::instance = nullptr;
|
||||||
|
|
||||||
Settings::~Settings() {
|
Settings::~Settings() {
|
||||||
delete defaults;
|
|
||||||
delete zcashconf;
|
|
||||||
delete uisettings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::getSaveZtxs() {
|
bool Settings::getSaveZtxs() {
|
||||||
@@ -24,27 +21,6 @@ Settings* Settings::init() {
|
|||||||
if (instance == nullptr)
|
if (instance == nullptr)
|
||||||
instance = new Settings();
|
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;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,23 +28,7 @@ Settings* Settings::getInstance() {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Config Settings::getSettings() {
|
||||||
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;
|
|
||||||
|
|
||||||
// Load from the QT Settings.
|
// Load from the QT Settings.
|
||||||
QSettings s;
|
QSettings s;
|
||||||
|
|
||||||
@@ -77,9 +37,7 @@ bool Settings::loadFromSettings() {
|
|||||||
auto username = s.value("connection/rpcuser").toString();
|
auto username = s.value("connection/rpcuser").toString();
|
||||||
auto password = s.value("connection/rpcpassword").toString();
|
auto password = s.value("connection/rpcpassword").toString();
|
||||||
|
|
||||||
uisettings = new Config{host, port, username, password};
|
return Config{host, port, username, password};
|
||||||
|
|
||||||
return !username.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -96,63 +54,9 @@ void Settings::saveSettings(const QString& host, const QString& port, const QStr
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::loadFromFile() {
|
void Settings::setUsingZcashConf(QString confLocation) {
|
||||||
delete zcashconf;
|
if (!confLocation.isEmpty())
|
||||||
|
_confLocation = confLocation;
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::isTestnet() {
|
bool Settings::isTestnet() {
|
||||||
|
|||||||
@@ -16,14 +16,8 @@ public:
|
|||||||
static Settings* init();
|
static Settings* init();
|
||||||
static Settings* getInstance();
|
static Settings* getInstance();
|
||||||
|
|
||||||
QString getUsernamePassword();
|
Config getSettings();
|
||||||
QString getHost();
|
void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password);
|
||||||
QString getPort();
|
|
||||||
|
|
||||||
bool loadFromSettings();
|
|
||||||
bool loadFromFile();
|
|
||||||
|
|
||||||
void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password);
|
|
||||||
|
|
||||||
bool isTestnet();
|
bool isTestnet();
|
||||||
void setTestnet(bool isTestnet);
|
void setTestnet(bool isTestnet);
|
||||||
@@ -43,11 +37,13 @@ public:
|
|||||||
|
|
||||||
bool isSaplingActive();
|
bool isSaplingActive();
|
||||||
|
|
||||||
const QString& getZcashdConfLocation() { return confLocation; }
|
void setUsingZcashConf(QString confLocation);
|
||||||
|
const QString& getZcashdConfLocation() { return _confLocation; }
|
||||||
|
|
||||||
void setZECPrice(double p) { zecPrice = p; }
|
void setZECPrice(double p) { zecPrice = p; }
|
||||||
double getZECPrice();
|
double getZECPrice();
|
||||||
|
|
||||||
|
|
||||||
QString getUSDFormat (double bal);
|
QString getUSDFormat (double bal);
|
||||||
QString getZECDisplayFormat (double bal);
|
QString getZECDisplayFormat (double bal);
|
||||||
QString getZECUSDDisplayFormat(double bal);
|
QString getZECUSDDisplayFormat(double bal);
|
||||||
@@ -59,17 +55,10 @@ private:
|
|||||||
|
|
||||||
static Settings* instance;
|
static Settings* instance;
|
||||||
|
|
||||||
Config* currentConfig;
|
QString _confLocation;
|
||||||
|
bool _isTestnet = false;
|
||||||
Config* defaults = nullptr;
|
bool _isSyncing = false;
|
||||||
Config* zcashconf = nullptr;
|
int _blockNumber = 0;
|
||||||
Config* uisettings = nullptr;
|
|
||||||
|
|
||||||
QString confLocation;
|
|
||||||
|
|
||||||
bool _isTestnet = false;
|
|
||||||
bool _isSyncing = false;
|
|
||||||
int _blockNumber = 0;
|
|
||||||
|
|
||||||
double zecPrice = 0.0;
|
double zecPrice = 0.0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>1</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="hostname">
|
<widget class="QLineEdit" name="hostname">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
<string>127.0.0.1</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="port">
|
<widget class="QLineEdit" name="port">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
<string>8232</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ void Turnstile::planMigration(QString zaddr, QString destAddr, int numsplits, in
|
|||||||
auto splits = splitAmount(bal, numsplits);
|
auto splits = splitAmount(bal, numsplits);
|
||||||
|
|
||||||
// Then, generate an intermediate t-Address for each part using getBatchRPC
|
// Then, generate an intermediate t-Address for each part using getBatchRPC
|
||||||
rpc->getBatchRPC<double>(splits,
|
rpc->getConnection()->doBatchRPC<double>(splits,
|
||||||
[=] (double /*unused*/) {
|
[=] (double /*unused*/) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ SOURCES += \
|
|||||||
src/txtablemodel.cpp \
|
src/txtablemodel.cpp \
|
||||||
src/turnstile.cpp \
|
src/turnstile.cpp \
|
||||||
src/utils.cpp \
|
src/utils.cpp \
|
||||||
src/qrcodelabel.cpp
|
src/qrcodelabel.cpp \
|
||||||
|
src/connection.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/mainwindow.h \
|
src/mainwindow.h \
|
||||||
@@ -70,7 +71,8 @@ HEADERS += \
|
|||||||
src/senttxstore.h \
|
src/senttxstore.h \
|
||||||
src/turnstile.h \
|
src/turnstile.h \
|
||||||
src/utils.h \
|
src/utils.h \
|
||||||
src/qrcodelabel.h
|
src/qrcodelabel.h \
|
||||||
|
src/connection.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
src/mainwindow.ui \
|
src/mainwindow.ui \
|
||||||
|
|||||||
Reference in New Issue
Block a user