Browse Source

Merge pull request #22 from adityapk00/connection

Separate out RPC to prep for embedded zcashd
import_zecw
adityapk00 6 years ago
committed by GitHub
parent
commit
52ecfa3498
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 319
      src/connection.cpp
  2. 125
      src/connection.h
  3. 64
      src/connection.ui
  4. 3
      src/main.cpp
  5. 22
      src/mainwindow.cpp
  6. 2
      src/mainwindow.h
  7. 4
      src/precompiled.h
  8. 227
      src/rpc.cpp
  9. 67
      src/rpc.h
  10. 106
      src/settings.cpp
  11. 29
      src/settings.h
  12. 6
      src/settings.ui
  13. 2
      src/turnstile.cpp
  14. 6
      zec-qt-wallet.pro

319
src/connection.cpp

@ -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

@ -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

64
src/connection.ui

@ -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"> <widget class="QLabel" name="status">
<property name="value"> <property name="text">
<number>24</number> <string>Connection Status</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0" rowspan="2">
<widget class="QLabel" name="status"> <widget class="QLabel" name="icon">
<property name="text"> <property name="text">
<string>Connection Status</string> <string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>20</number>
</property> </property>
</widget> </widget>
</item> </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/>

3
src/main.cpp

@ -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();

22
src/mainwindow.cpp

@ -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,13 +63,11 @@ 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;
@ -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);
}; };
}); });

2
src/mainwindow.h

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

4
src/precompiled.h

@ -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"

227
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;
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) { delete conn;
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump())); this->conn = c;
QObject::connect(reply, &QNetworkReply::finished, [=] { refresh();
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"},
@ -712,13 +599,16 @@ void RPC::refreshSentZTrans() {
); );
} }
void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) { void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) {
watchingOps.insert(newOpid, tx); watchingOps.insert(newOpid, tx);
watchTxStatus(); watchTxStatus();
} }
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

@ -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

@ -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() {

29
src/settings.h

@ -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;
}; };

6
src/settings.ui

@ -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>

2
src/turnstile.cpp

@ -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"},

6
zec-qt-wallet.pro

@ -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 \

Loading…
Cancel
Save