Browse Source

Move all RPC to connection class

recurring
Aditya Kulkarni 6 years ago
parent
commit
68aeca7f09
  1. 43
      src/connection.cpp
  2. 60
      src/connection.h
  3. 161
      src/rpc.cpp
  4. 56
      src/rpc.h
  5. 2
      src/turnstile.cpp

43
src/connection.cpp

@ -90,7 +90,7 @@ Connection* ConnectionLoader::makeConnection(std::shared_ptr<ConnectionConfig> c
QString headerData = "Basic " + userpass.toLocal8Bit().toBase64();
request->setRawHeader("Authorization", headerData.toLocal8Bit());
return new Connection(client, request, config);
return new Connection(main, client, request, config);
}
void ConnectionLoader::refreshZcashdState(Connection* connection) {
@ -105,7 +105,8 @@ void ConnectionLoader::refreshZcashdState(Connection* connection) {
d->hide();
rpc->setConnection(connection);
},
[=] (auto err, auto res) {
[=] (auto reply, auto res) {
auto err = reply->error();
// Failed, see what it is.
qDebug() << err << ":" << QString::fromStdString(res.dump());
@ -251,10 +252,11 @@ std::shared_ptr<ConnectionConfig> ConnectionLoader::loadFromSettings() {
Connection::Connection(QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr<ConnectionConfig> conf) {
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() {
@ -265,7 +267,7 @@ Connection::~Connection() {
void Connection::doRPC(const json& payload, const std::function<void(json)>& cb,
const std::function<void(QNetworkReply::NetworkError, const json&)>& ne) {
const std::function<void(QNetworkReply*, const json&)>& ne) {
QNetworkReply *reply = restclient->post(*request, QByteArray::fromStdString(payload.dump()));
QObject::connect(reply, &QNetworkReply::finished, [=] {
@ -273,16 +275,45 @@ void Connection::doRPC(const json& payload, const std::function<void(json)>& cb,
if (reply->error() != QNetworkReply::NoError) {
auto parsed = json::parse(reply->readAll(), nullptr, false);
ne(reply->error(), parsed);
ne(reply, parsed);
return;
}
auto parsed = json::parse(reply->readAll(), nullptr, false);
if (parsed.is_discarded()) {
ne(reply->error(), "Unknown error");
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();
}

60
src/connection.h

@ -1,12 +1,12 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include "mainwindow.h"
#include "ui_connection.h"
#include "precompiled.h"
using json = nlohmann::json;
class MainWindow;
class RPC;
enum ConnectionType {
@ -59,17 +59,67 @@ private:
*/
class Connection {
public:
Connection(QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr<ConnectionConfig> conf);
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::NetworkError, const json&)>& ne);
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

161
src/rpc.cpp

@ -71,32 +71,6 @@ void RPC::setConnection(Connection* c) {
refresh();
}
void RPC::doRPC(const json& payload, const std::function<void(json)>& cb) {
QNetworkReply *reply = conn->restclient->post(*conn->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) {
json payload = {
{"jsonrpc", "1.0"},
@ -104,7 +78,7 @@ void RPC::getZAddresses(const std::function<void(json)>& cb) {
{"method", "z_listaddresses"},
};
doRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::getTransparentUnspent(const std::function<void(json)>& cb) {
@ -115,7 +89,7 @@ void RPC::getTransparentUnspent(const std::function<void(json)>& cb) {
{"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) {
@ -126,7 +100,7 @@ void RPC::getZUnspent(const std::function<void(json)>& cb) {
{"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) {
@ -137,7 +111,7 @@ void RPC::newZaddr(bool sapling, const std::function<void(json)>& cb) {
{"params", { sapling ? "sapling" : "sprout" }},
};
doRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::newTaddr(const std::function<void(json)>& cb) {
@ -147,7 +121,7 @@ void RPC::newTaddr(const std::function<void(json)>& cb) {
{"method", "getnewaddress"},
};
doRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::getZPrivKey(QString addr, const std::function<void(json)>& cb) {
@ -158,7 +132,7 @@ void RPC::getZPrivKey(QString addr, const std::function<void(json)>& cb) {
{"params", { addr.toStdString() }},
};
doRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::getTPrivKey(QString addr, const std::function<void(json)>& cb) {
@ -169,7 +143,7 @@ void RPC::getTPrivKey(QString addr, const std::function<void(json)>& cb) {
{"params", { addr.toStdString() }},
};
doRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::importZPrivKey(QString addr, bool rescan, const std::function<void(json)>& cb) {
@ -180,7 +154,7 @@ void RPC::importZPrivKey(QString addr, bool rescan, const std::function<void(jso
{"params", { addr.toStdString(), (rescan? "yes" : "no") }},
};
doSendRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
@ -192,7 +166,7 @@ void RPC::importTPrivKey(QString addr, bool rescan, const std::function<void(jso
{"params", { addr.toStdString(), (rescan? "yes" : "no") }},
};
doSendRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
@ -204,7 +178,7 @@ void RPC::getBalance(const std::function<void(json)>& cb) {
{"params", {0}} // Get Unconfirmed balance as well.
};
doRPC(payload, cb);
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::getTransactions(const std::function<void(json)>& cb) {
@ -214,39 +188,7 @@ void RPC::getTransactions(const std::function<void(json)>& cb) {
{"method", "listtransactions"}
};
doRPC(payload, cb);
}
void RPC::doSendRPC(const json& payload, const std::function<void(json)>& cb, const std::function<void(QString)>& err) {
QNetworkReply *reply = conn->restclient->post(*conn->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); });
conn->doRPCWithDefaultErrorHandling(payload, cb);
}
void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
@ -257,76 +199,9 @@ void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
{"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)
@ -377,7 +252,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
// and each z-Addr can have multiple received txs.
// 1. For each z-Addr, get list of received txs
getBatchRPC<QString>(zaddrs,
conn->doBatchRPC<QString>(zaddrs,
[=] (QString zaddr) {
json payload = {
{"jsonrpc", "1.0"},
@ -414,7 +289,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
}
// 2. For all txids, go and get the details of that txid.
getBatchRPC<QString>(txids.toList(),
conn->doBatchRPC<QString>(txids.toList(),
[=] (QString txid) {
json payload = {
{"jsonrpc", "1.0"},
@ -488,7 +363,7 @@ void RPC::getInfoThenRefresh(bool force) {
{"method", "getinfo"}
};
doRPC(payload, [=] (const json& reply) {
conn->doRPCIgnoreError(payload, [=] (const json& reply) {
// Testnet?
if (!reply["testnet"].is_null()) {
Settings::getInstance()->setTestnet(reply["testnet"].get<json::boolean_t>());
@ -517,7 +392,7 @@ void RPC::getInfoThenRefresh(bool force) {
{"method", "getblockchaininfo"}
};
doRPC(payload, [=](const json& reply) {
conn->doRPCIgnoreError(payload, [=](const json& reply) {
auto progress = reply["verificationprogress"].get<double>();
bool isSyncing = progress < 0.999; // 99.9%
int blockNumber = reply["blocks"].get<json::number_unsigned_t>();
@ -693,7 +568,7 @@ void RPC::refreshSentZTrans() {
}
// Look up all the txids to get the confirmation count for them.
getBatchRPC<QString>(txids,
conn->doBatchRPC<QString>(txids,
[=] (QString txid) {
json payload = {
{"jsonrpc", "1.0"},
@ -741,7 +616,7 @@ void RPC::watchTxStatus() {
{"method", "z_getoperationstatus"},
};
doRPC(payload, [=] (const json& reply) {
conn->doRPCWithDefaultErrorHandling(payload, [=] (const json& reply) {
// There's an array for each item in the status
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

56
src/rpc.h

@ -56,64 +56,12 @@ public:
void importZPrivKey(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; }
// 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 = conn->restclient->post(*conn->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);
}
Turnstile* getTurnstile() { return turnstile; }
Connection* getConnection() { return conn; }
private:
void noConnection();
void doRPC (const json& payload, const std::function<void(json)>& cb);
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 refreshTransactions();

2
src/turnstile.cpp

@ -91,7 +91,7 @@ void Turnstile::planMigration(QString zaddr, QString destAddr, int numsplits, in
auto splits = splitAmount(bal, numsplits);
// Then, generate an intermediate t-Address for each part using getBatchRPC
rpc->getBatchRPC<double>(splits,
rpc->getConnection()->doBatchRPC<double>(splits,
[=] (double /*unused*/) {
json payload = {
{"jsonrpc", "1.0"},

Loading…
Cancel
Save