Browse Source

formatting

import_zecw
adityapk00 6 years ago
parent
commit
e1b9ca0150
  1. 192
      src/balancestablemodel.cpp
  2. 10
      src/main.cpp
  3. 280
      src/mainwindow.cpp
  4. 22
      src/mainwindow.h
  5. 210
      src/rpc.cpp
  6. 10
      src/rpc.h
  7. 144
      src/sendtab.cpp
  8. 10
      src/senttxstore.cpp
  9. 222
      src/settings.cpp
  10. 66
      src/settings.h
  11. 546
      src/turnstile.cpp
  12. 74
      src/turnstile.h
  13. 26
      src/txtablemodel.cpp
  14. 26
      src/utils.h

192
src/balancestablemodel.cpp

@ -3,128 +3,128 @@
#include "utils.h"
BalancesTableModel::BalancesTableModel(QObject *parent)
: QAbstractTableModel(parent) {
: QAbstractTableModel(parent) {
}
void BalancesTableModel::setNewData(const QMap<QString, double>* balances,
const QList<UnspentOutput>* outputs)
{
loading = false;
int currentRows = rowCount(QModelIndex());
// Copy over the utxos for our use
delete utxos;
utxos = new QList<UnspentOutput>();
// This is a QList deep copy.
*utxos = *outputs;
// Process the address balances into a list
delete modeldata;
modeldata = new QList<std::tuple<QString, QString>>();
std::for_each(balances->keyBegin(), balances->keyEnd(), [=] (auto keyIt) {
modeldata->push_back(std::make_tuple(keyIt, QString::number(balances->value(keyIt), 'g', 8)));
});
// And then update the data
dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1));
// Change the layout only if the number of rows changed
if (modeldata && modeldata->size() != currentRows)
layoutChanged();
const QList<UnspentOutput>* outputs)
{
loading = false;
int currentRows = rowCount(QModelIndex());
// Copy over the utxos for our use
delete utxos;
utxos = new QList<UnspentOutput>();
// This is a QList deep copy.
*utxos = *outputs;
// Process the address balances into a list
delete modeldata;
modeldata = new QList<std::tuple<QString, QString>>();
std::for_each(balances->keyBegin(), balances->keyEnd(), [=] (auto keyIt) {
modeldata->push_back(std::make_tuple(keyIt, QString::number(balances->value(keyIt), 'g', 8)));
});
// And then update the data
dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1));
// Change the layout only if the number of rows changed
if (modeldata && modeldata->size() != currentRows)
layoutChanged();
}
BalancesTableModel::~BalancesTableModel() {
delete modeldata;
delete utxos;
delete modeldata;
delete utxos;
}
int BalancesTableModel::rowCount(const QModelIndex&) const
{
if (modeldata == nullptr) {
if (loading)
return 1;
else
return 0;
}
return modeldata->size();
if (modeldata == nullptr) {
if (loading)
return 1;
else
return 0;
}
return modeldata->size();
}
int BalancesTableModel::columnCount(const QModelIndex&) const
{
return 2;
return 2;
}
QVariant BalancesTableModel::data(const QModelIndex &index, int role) const
{
if (loading) {
if (role == Qt::DisplayRole)
return "Loading...";
else
return QVariant();
}
if (loading) {
if (role == Qt::DisplayRole)
return "Loading...";
else
return QVariant();
}
if (role == Qt::TextAlignmentRole && index.column() == 1) return QVariant(Qt::AlignRight | Qt::AlignVCenter);
if (role == Qt::ForegroundRole) {
// If any of the UTXOs for this address has zero confirmations, paint it in red
if (role == Qt::ForegroundRole) {
// If any of the UTXOs for this address has zero confirmations, paint it in red
const auto& addr = std::get<0>(modeldata->at(index.row()));
for (auto utxo : *utxos) {
if (utxo.address == addr && utxo.confirmations == 0) {
QBrush b;
b.setColor(Qt::red);
return b;
}
}
// Else, just return the default brush
QBrush b;
b.setColor(Qt::black);
return b;
}
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: return std::get<0>(modeldata->at(index.row()));
case 1: return Settings::getInstance()->getZECDisplayFormat(std::get<1>(modeldata->at(index.row())).toDouble());
}
}
if(role == Qt::ToolTipRole) {
switch (index.column()) {
case 0: return std::get<0>(modeldata->at(index.row()));
case 1: {
auto bal = std::get<1>(modeldata->at(index.row())).toDouble();
return Settings::getInstance()->getUSDFormat(bal);
}
}
}
return QVariant();
for (auto utxo : *utxos) {
if (utxo.address == addr && utxo.confirmations == 0) {
QBrush b;
b.setColor(Qt::red);
return b;
}
}
// Else, just return the default brush
QBrush b;++
b.setColor(Qt::black);
return b;
}
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: return std::get<0>(modeldata->at(index.row()));
case 1: return Settings::getInstance()->getZECDisplayFormat(std::get<1>(modeldata->at(index.row())).toDouble());
}
}
if(role == Qt::ToolTipRole) {
switch (index.column()) {
case 0: return std::get<0>(modeldata->at(index.row()));
case 1: {
auto bal = std::get<1>(modeldata->at(index.row())).toDouble();
return Settings::getInstance()->getUSDFormat(bal);
}
}
}
return QVariant();
}
QVariant BalancesTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::TextAlignmentRole && section == 1) {
if (role == Qt::TextAlignmentRole && section == 1) {
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
}
if (role == Qt::FontRole) {
QFont f;
f.setBold(true);
return f;
}
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
switch (section) {
case 0: return tr("Address");
case 1: return tr("Amount");
default: return QVariant();
}
}
return QVariant();
}
if (role == Qt::FontRole) {
QFont f;
f.setBold(true);
return f;
}
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
switch (section) {
case 0: return tr("Address");
case 1: return tr("Amount");
default: return QVariant();
}
}
return QVariant();
}

10
src/main.cpp

@ -6,8 +6,8 @@
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication a(argc, argv);
QIcon icon(":/icons/res/icon.ico");
@ -18,10 +18,10 @@ int main(int argc, char *argv[])
qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false));
#endif
std::srand(std::time(nullptr));
std::srand(std::time(nullptr));
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
QCoreApplication::setApplicationName("zec-qt-wallet");
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
QCoreApplication::setApplicationName("zec-qt-wallet");
Settings::init();

280
src/mainwindow.cpp

@ -22,11 +22,11 @@ MainWindow::MainWindow(QWidget *parent) :
{
ui->setupUi(this);
// Status Bar
setupStatusBar();
// Status Bar
setupStatusBar();
// Settings editor
setupSettingsModal();
// Settings editor
setupSettingsModal();
// Set up exit action
QObject::connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
@ -45,10 +45,10 @@ MainWindow::MainWindow(QWidget *parent) :
QObject::connect(ui->actionAbout, &QAction::triggered, [=] () {
QDialog aboutDialog(this);
Ui_about about;
about.setupUi(&aboutDialog);
about.setupUi(&aboutDialog);
QString version = QString("Version ") % QString(APP_VERSION) % " (" % QString(__DATE__) % ")";
about.versionLabel->setText(version);
QString version = QString("Version ") % QString(APP_VERSION) % " (" % QString(__DATE__) % ")";
about.versionLabel->setText(version);
aboutDialog.exec();
});
@ -67,25 +67,25 @@ MainWindow::MainWindow(QWidget *parent) :
rpc->refresh(true); // Force refresh first time
restoreSavedStates();
restoreSavedStates();
}
void MainWindow::restoreSavedStates() {
QSettings s;
restoreGeometry(s.value("geometry").toByteArray());
QSettings s;
restoreGeometry(s.value("geometry").toByteArray());
ui->balancesTable->horizontalHeader()->restoreState(s.value("baltablegeometry").toByteArray());
ui->transactionsTable->horizontalHeader()->restoreState(s.value("tratablegeometry").toByteArray());
ui->balancesTable->horizontalHeader()->restoreState(s.value("baltablegeometry").toByteArray());
ui->transactionsTable->horizontalHeader()->restoreState(s.value("tratablegeometry").toByteArray());
}
void MainWindow::closeEvent(QCloseEvent* event) {
QSettings s;
QSettings s;
s.setValue("geometry", saveGeometry());
s.setValue("baltablegeometry", ui->balancesTable->horizontalHeader()->saveState());
s.setValue("tratablegeometry", ui->transactionsTable->horizontalHeader()->saveState());
s.setValue("geometry", saveGeometry());
s.setValue("baltablegeometry", ui->balancesTable->horizontalHeader()->saveState());
s.setValue("tratablegeometry", ui->transactionsTable->horizontalHeader()->saveState());
QMainWindow::closeEvent(event);
QMainWindow::closeEvent(event);
}
void MainWindow::turnstileProgress() {
@ -106,8 +106,8 @@ void MainWindow::turnstileProgress() {
auto nextTxBlock = curProgress.nextBlock - Settings::getInstance()->getBlockNumber();
progress.fromAddr->setText(curProgress.from);
progress.toAddr->setText(curProgress.to);
progress.fromAddr->setText(curProgress.from);
progress.toAddr->setText(curProgress.to);
if (curProgress.step == curProgress.totalSteps) {
auto txt = QString("Turnstile migration finished");
@ -272,126 +272,126 @@ void MainWindow::setupTurnstileDialog() {
}
void MainWindow::setupStatusBar() {
// Status Bar
loadingLabel = new QLabel();
loadingMovie = new QMovie(":/icons/res/loading.gif");
loadingMovie->setScaledSize(QSize(32, 16));
loadingMovie->start();
loadingLabel->setAttribute(Qt::WA_NoSystemBackground);
loadingLabel->setMovie(loadingMovie);
ui->statusBar->addPermanentWidget(loadingLabel);
loadingLabel->setVisible(false);
// Custom status bar menu
ui->statusBar->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->statusBar, &QStatusBar::customContextMenuRequested, [=](QPoint pos) {
auto msg = ui->statusBar->currentMessage();
QMenu menu(this);
if (!msg.isEmpty() && msg.startsWith(Utils::txidStatusMessage)) {
auto txid = msg.split(":")[1].trimmed();
menu.addAction("Copy txid", [=]() {
QGuiApplication::clipboard()->setText(txid);
});
menu.addAction("View tx on block explorer", [=]() {
QString url;
if (Settings::getInstance()->isTestnet()) {
url = "https://explorer.testnet.z.cash/tx/" + txid;
}
else {
url = "https://explorer.zcha.in/transactions/" + txid;
}
QDesktopServices::openUrl(QUrl(url));
});
}
menu.addAction("Refresh", [=]() {
rpc->refresh(true);
});
QPoint gpos(mapToGlobal(pos).x(), mapToGlobal(pos).y() + this->height() - ui->statusBar->height());
menu.exec(gpos);
});
statusLabel = new QLabel();
ui->statusBar->addPermanentWidget(statusLabel);
statusIcon = new QLabel();
ui->statusBar->addPermanentWidget(statusIcon);
// Status Bar
loadingLabel = new QLabel();
loadingMovie = new QMovie(":/icons/res/loading.gif");
loadingMovie->setScaledSize(QSize(32, 16));
loadingMovie->start();
loadingLabel->setAttribute(Qt::WA_NoSystemBackground);
loadingLabel->setMovie(loadingMovie);
ui->statusBar->addPermanentWidget(loadingLabel);
loadingLabel->setVisible(false);
// Custom status bar menu
ui->statusBar->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->statusBar, &QStatusBar::customContextMenuRequested, [=](QPoint pos) {
auto msg = ui->statusBar->currentMessage();
QMenu menu(this);
if (!msg.isEmpty() && msg.startsWith(Utils::txidStatusMessage)) {
auto txid = msg.split(":")[1].trimmed();
menu.addAction("Copy txid", [=]() {
QGuiApplication::clipboard()->setText(txid);
});
menu.addAction("View tx on block explorer", [=]() {
QString url;
if (Settings::getInstance()->isTestnet()) {
url = "https://explorer.testnet.z.cash/tx/" + txid;
}
else {
url = "https://explorer.zcha.in/transactions/" + txid;
}
QDesktopServices::openUrl(QUrl(url));
});
}
menu.addAction("Refresh", [=]() {
rpc->refresh(true);
});
QPoint gpos(mapToGlobal(pos).x(), mapToGlobal(pos).y() + this->height() - ui->statusBar->height());
menu.exec(gpos);
});
statusLabel = new QLabel();
ui->statusBar->addPermanentWidget(statusLabel);
statusIcon = new QLabel();
ui->statusBar->addPermanentWidget(statusIcon);
}
void MainWindow::setupSettingsModal() {
// Set up File -> Settings action
QObject::connect(ui->actionSettings, &QAction::triggered, [=]() {
QDialog settingsDialog(this);
Ui_Settings settings;
settings.setupUi(&settingsDialog);
// Setup save sent check box
QObject::connect(settings.chkSaveTxs, &QCheckBox::stateChanged, [=](auto checked) {
Settings::getInstance()->setSaveZtxs(checked);
});
// Setup clear button
QObject::connect(settings.btnClearSaved, &QCheckBox::clicked, [=]() {
if (QMessageBox::warning(this, "Clear saved history?",
"Shielded z-Address transactions are stored locally in your wallet, outside zcashd. You may delete this saved information safely any time for your privacy.\nDo you want to delete the saved shielded transactions now ?",
QMessageBox::Yes, QMessageBox::Cancel)) {
SentTxStore::deleteHistory();
// Reload after the clear button so existing txs disappear
rpc->refresh(true);
}
});
// Save sent transactions
settings.chkSaveTxs->setChecked(Settings::getInstance()->getSaveZtxs());
// Connection Settings
QIntValidator validator(0, 65535);
settings.port->setValidator(&validator);
// Load current values into the dialog
void MainWindow::setupSettingsModal() {
// Set up File -> Settings action
QObject::connect(ui->actionSettings, &QAction::triggered, [=]() {
QDialog settingsDialog(this);
Ui_Settings settings;
settings.setupUi(&settingsDialog);
// Setup save sent check box
QObject::connect(settings.chkSaveTxs, &QCheckBox::stateChanged, [=](auto checked) {
Settings::getInstance()->setSaveZtxs(checked);
});
// Setup clear button
QObject::connect(settings.btnClearSaved, &QCheckBox::clicked, [=]() {
if (QMessageBox::warning(this, "Clear saved history?",
"Shielded z-Address transactions are stored locally in your wallet, outside zcashd. You may delete this saved information safely any time for your privacy.\nDo you want to delete the saved shielded transactions now ?",
QMessageBox::Yes, QMessageBox::Cancel)) {
SentTxStore::deleteHistory();
// Reload after the clear button so existing txs disappear
rpc->refresh(true);
}
});
// Save sent transactions
settings.chkSaveTxs->setChecked(Settings::getInstance()->getSaveZtxs());
// Connection Settings
QIntValidator validator(0, 65535);
settings.port->setValidator(&validator);
// Load current values into the dialog
settings.hostname->setText(Settings::getInstance()->getHost());
settings.port->setText(Settings::getInstance()->getPort());
settings.rpcuser->setText(Settings::getInstance()->getUsernamePassword().split(":")[0]);
settings.rpcpassword->setText(Settings::getInstance()->getUsernamePassword().split(":")[1]);
// If values are coming from zcash.conf, then disable all the fields
auto zcashConfLocation = Settings::getInstance()->getZcashdConfLocation();
if (!zcashConfLocation.isEmpty()) {
settings.confMsg->setText("Settings are being read from \n" + zcashConfLocation);
settings.hostname->setEnabled(false);
settings.port->setEnabled(false);
settings.rpcuser->setEnabled(false);
settings.rpcpassword->setEnabled(false);
}
else {
settings.confMsg->setText("No local zcash.conf found. Please configure connection manually.");
settings.hostname->setEnabled(true);
settings.port->setEnabled(true);
settings.rpcuser->setEnabled(true);
settings.rpcpassword->setEnabled(true);
}
// Connection tab by default
settings.tabWidget->setCurrentIndex(0);
if (settingsDialog.exec() == QDialog::Accepted) {
if (zcashConfLocation.isEmpty()) {
// Save settings
Settings::getInstance()->saveSettings(
settings.hostname->text(),
settings.port->text(),
settings.rpcuser->text(),
settings.rpcpassword->text());
// If values are coming from zcash.conf, then disable all the fields
auto zcashConfLocation = Settings::getInstance()->getZcashdConfLocation();
if (!zcashConfLocation.isEmpty()) {
settings.confMsg->setText("Settings are being read from \n" + zcashConfLocation);
settings.hostname->setEnabled(false);
settings.port->setEnabled(false);
settings.rpcuser->setEnabled(false);
settings.rpcpassword->setEnabled(false);
}
else {
settings.confMsg->setText("No local zcash.conf found. Please configure connection manually.");
settings.hostname->setEnabled(true);
settings.port->setEnabled(true);
settings.rpcuser->setEnabled(true);
settings.rpcpassword->setEnabled(true);
}
// Connection tab by default
settings.tabWidget->setCurrentIndex(0);
if (settingsDialog.exec() == QDialog::Accepted) {
if (zcashConfLocation.isEmpty()) {
// Save settings
Settings::getInstance()->saveSettings(
settings.hostname->text(),
settings.port->text(),
settings.rpcuser->text(),
settings.rpcpassword->text());
this->rpc->reloadConnectionInfo();
}
// Then refresh everything.
this->rpc->refresh(true);
};
});
// Then refresh everything.
this->rpc->refresh(true);
};
});
}
@ -504,18 +504,18 @@ void MainWindow::setupBalancesTab() {
rpc->getTPrivKey(addr, fnCB);
});
menu.addAction("Send from " % addr.left(40) % (addr.size() > 40 ? "..." : ""), [=]() {
// Find the inputs combo
for (int i = 0; i < ui->inputsCombo->count(); i++) {
if (ui->inputsCombo->itemText(i).startsWith(addr)) {
ui->inputsCombo->setCurrentIndex(i);
break;
}
}
// And switch to the send tab.
ui->tabWidget->setCurrentIndex(1);
});
menu.addAction("Send from " % addr.left(40) % (addr.size() > 40 ? "..." : ""), [=]() {
// Find the inputs combo
for (int i = 0; i < ui->inputsCombo->count(); i++) {
if (ui->inputsCombo->itemText(i).startsWith(addr)) {
ui->inputsCombo->setCurrentIndex(i);
break;
}
}
// And switch to the send tab.
ui->tabWidget->setCurrentIndex(1);
});
if (addr.startsWith("t")) {
menu.addAction("View on block explorer", [=] () {

22
src/mainwindow.h

@ -43,7 +43,7 @@ public:
QLabel* loadingLabel;
private:
void closeEvent(QCloseEvent* event);
void closeEvent(QCloseEvent* event);
void setupSendTab();
void setupTransactionsTab();
@ -51,11 +51,11 @@ private:
void setupBalancesTab();
void setupTurnstileDialog();
void setupSettingsModal();
void setupStatusBar();
void setupSettingsModal();
void setupStatusBar();
void removeExtraAddresses();
void setDefaultPayFrom();
void removeExtraAddresses();
void setDefaultPayFrom();
Tx createTxFromSendPage();
bool confirmTx(Tx tx, ToFields devFee);
@ -63,11 +63,11 @@ private:
void turnstileDoMigration(QString fromAddr = "");
void turnstileProgress();
void cancelButton();
void sendButton();
void inputComboTextChanged(const QString& text);
void addAddressSection();
void maxAmountChecked(int checked);
void cancelButton();
void sendButton();
void inputComboTextChanged(const QString& text);
void addAddressSection();
void maxAmountChecked(int checked);
void addressChanged(int number, const QString& text);
void amountChanged (int numer, const QString& text);
@ -84,7 +84,7 @@ private:
void importPrivKey();
void doImport(QList<QString>* keys);
void restoreSavedStates();
void restoreSavedStates();
RPC* rpc;

210
src/rpc.cpp

@ -7,9 +7,9 @@
using json = nlohmann::json;
RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
this->restclient = client;
this->main = main;
this->ui = main->ui;
this->restclient = client;
this->main = main;
this->ui = main->ui;
this->turnstile = new Turnstile(this, main);
@ -26,7 +26,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
main->ui->transactionsTable->setColumnWidth(2, 200);
main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
reloadConnectionInfo();
reloadConnectionInfo();
// Set up timer to refresh Price
priceTimer = new QTimer(main);
@ -35,12 +35,12 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
});
priceTimer->start(Utils::priceRefreshSpeed); // Every hour
// Set up a timer to refresh the UI every few seconds
timer = new QTimer(main);
QObject::connect(timer, &QTimer::timeout, [=]() {
refresh();
});
timer->start(Utils::updateSpeed);
// Set up a timer to refresh the UI every few seconds
timer = new QTimer(main);
QObject::connect(timer, &QTimer::timeout, [=]() {
refresh();
});
timer->start(Utils::updateSpeed);
// Set up the timer to watch for tx status
txTimer = new QTimer(main);
@ -68,9 +68,9 @@ RPC::~RPC() {
}
void RPC::reloadConnectionInfo() {
// Reset for any errors caused.
firstTime = true;
// Reset for any errors caused.
firstTime = true;
QUrl myurl;
myurl.setScheme("http"); //https also applicable
myurl.setHost(Settings::getInstance()->getHost());
@ -230,35 +230,35 @@ void RPC::getTransactions(const std::function<void(json)>& cb) {
}
void RPC::doSendRPC(const json& payload, const std::function<void(json)>& cb, const std::function<void(QString)>& err) {
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
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"]);
});
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); });
doSendRPC(payload, cb, [=](auto error) { this->handleTxError(error); });
}
void RPC::sendZTransaction(json params, const std::function<void(json)>& cb) {
@ -296,17 +296,17 @@ void RPC::handleConnectionError(const QString& error) {
% "\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?";
}
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)) {
@ -351,7 +351,7 @@ void RPC::fillTxJsonParams(json& params, Tx tx) {
for (int i=0; i < tx.toAddrs.size(); i++) {
auto toAddr = tx.toAddrs[i];
// Construct the JSON params
// Construct the JSON params
json rec = json::object();
rec["address"] = toAddr.addr.toStdString();
rec["amount"] = QString::number(toAddr.amount, 'f', 8).toDouble(); // Force it through string for rounding
@ -489,14 +489,14 @@ void RPC::getInfoThenRefresh(bool force) {
};
doRPC(payload, [=] (const json& reply) {
// Testnet?
if (!reply["testnet"].is_null()) {
Settings::getInstance()->setTestnet(reply["testnet"].get<json::boolean_t>());
};
// Testnet?
if (!reply["testnet"].is_null()) {
Settings::getInstance()->setTestnet(reply["testnet"].get<json::boolean_t>());
};
// Connected, so display checkmark.
QIcon i(":/icons/res/connected.png");
main->statusIcon->setPixmap(i.pixmap(16, 16));
// Connected, so display checkmark.
QIcon i(":/icons/res/connected.png");
main->statusIcon->setPixmap(i.pixmap(16, 16));
static int lastBlock = 0;
int curBlock = reply["blocks"].get<json::number_integer_t>();
@ -505,19 +505,19 @@ void RPC::getInfoThenRefresh(bool force) {
// Something changed, so refresh everything.
lastBlock = curBlock;
refreshBalances();
refreshBalances();
refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans()
refreshTransactions();
}
// Call to see if the blockchain is syncing.
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
{"method", "getblockchaininfo"}
};
// Call to see if the blockchain is syncing.
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
{"method", "getblockchaininfo"}
};
doRPC(payload, [=](const json& reply) {
doRPC(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>();
@ -525,20 +525,20 @@ void RPC::getInfoThenRefresh(bool force) {
Settings::getInstance()->setSyncing(isSyncing);
Settings::getInstance()->setBlockNumber(blockNumber);
QString statusText = QString() %
(isSyncing ? "Syncing" : "Connected") %
" (" %
(Settings::getInstance()->isTestnet() ? "testnet:" : "") %
QString::number(blockNumber) %
(isSyncing ? ("/" % QString::number(progress*100, 'f', 0) % "%") : QString()) %
")";
main->statusLabel->setText(statusText);
QString statusText = QString() %
(isSyncing ? "Syncing" : "Connected") %
" (" %
(Settings::getInstance()->isTestnet() ? "testnet:" : "") %
QString::number(blockNumber) %
(isSyncing ? ("/" % QString::number(progress*100, 'f', 0) % "%") : QString()) %
")";
main->statusLabel->setText(statusText);
auto zecPrice = Settings::getInstance()->getUSDFormat(1);
if (!zecPrice.isEmpty()) {
main->statusLabel->setToolTip("1 ZEC = " + zecPrice);
main->statusIcon->setToolTip("1 ZEC = " + zecPrice);
}
});
});
});
}
@ -569,43 +569,43 @@ void RPC::updateUI(bool anyUnconfirmed) {
main->ui->actionTurnstile_Migration->setVisible(false);
}
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
// Update balances model data, which will update the table too
balancesTableModel->setNewData(allBalances, utxos);
// Update balances model data, which will update the table too
balancesTableModel->setNewData(allBalances, utxos);
// Add all the addresses into the inputs combo box
auto lastFromAddr = ui->inputsCombo->currentText().split("(")[0].trimmed();
// Add all the addresses into the inputs combo box
auto lastFromAddr = ui->inputsCombo->currentText().split("(")[0].trimmed();
ui->inputsCombo->clear();
auto i = allBalances->constBegin();
while (i != allBalances->constEnd()) {
QString item = i.key() % "(" % QString::number(i.value(), 'g', 8) % " " % Utils::getTokenName() % ")";
ui->inputsCombo->addItem(item);
if (item.startsWith(lastFromAddr)) ui->inputsCombo->setCurrentText(item);
ui->inputsCombo->clear();
auto i = allBalances->constBegin();
while (i != allBalances->constEnd()) {
QString item = i.key() % "(" % QString::number(i.value(), 'g', 8) % " " % Utils::getTokenName() % ")";
ui->inputsCombo->addItem(item);
if (item.startsWith(lastFromAddr)) ui->inputsCombo->setCurrentText(item);
++i;
}
++i;
}
};
// Function to process reply of the listunspent and z_listunspent API calls, used below.
bool RPC::processUnspent(const json& reply) {
bool anyUnconfirmed = false;
for (auto& it : reply.get<json::array_t>()) {
QString qsAddr = QString::fromStdString(it["address"]);
auto confirmations = it["confirmations"].get<json::number_unsigned_t>();
if (confirmations == 0) {
anyUnconfirmed = true;
}
utxos->push_back(
UnspentOutput{ qsAddr, QString::fromStdString(it["txid"]),
bool anyUnconfirmed = false;
for (auto& it : reply.get<json::array_t>()) {
QString qsAddr = QString::fromStdString(it["address"]);
auto confirmations = it["confirmations"].get<json::number_unsigned_t>();
if (confirmations == 0) {
anyUnconfirmed = true;
}
utxos->push_back(
UnspentOutput{ qsAddr, QString::fromStdString(it["txid"]),
QString::number(it["amount"].get<json::number_float_t>(), 'g', 8),
(int)confirmations, it["spendable"].get<json::boolean_t>() });
(*allBalances)[qsAddr] = (*allBalances)[qsAddr] + it["amount"].get<json::number_float_t>();
}
return anyUnconfirmed;
(*allBalances)[qsAddr] = (*allBalances)[qsAddr] + it["amount"].get<json::number_float_t>();
}
return anyUnconfirmed;
};
void RPC::refreshBalances() {
@ -648,10 +648,10 @@ void RPC::refreshTransactions() {
QList<TransactionItem> txdata;
for (auto& it : reply.get<json::array_t>()) {
double fee = 0;
if (!it["fee"].is_null()) {
fee = it["fee"].get<json::number_float_t>();
}
double fee = 0;
if (!it["fee"].is_null()) {
fee = it["fee"].get<json::number_float_t>();
}
TransactionItem tx{
QString::fromStdString(it["category"]),
@ -736,7 +736,7 @@ void RPC::watchTxStatus() {
QString status = QString::fromStdString(it["status"]);
if (status == "success") {
auto txid = QString::fromStdString(it["result"]["txid"]);
SentTxStore::addToSentTx(watchingOps.value(id), txid);
main->ui->statusBar->showMessage(Utils::txidStatusMessage + " " + txid);

10
src/rpc.h

@ -15,7 +15,7 @@ class Turnstile;
struct TransactionItem {
QString type;
qint64 datetime;
qint64 datetime;
QString address;
QString txid;
double amount;
@ -45,7 +45,7 @@ public:
const QList<UnspentOutput>* getUTXOs() { return utxos; }
const QMap<QString, double>* getAllBalances() { return allBalances; }
void reloadConnectionInfo();
void reloadConnectionInfo();
void newZaddr(bool sapling, const std::function<void(json)>& cb);
void newTaddr(const std::function<void(json)>& cb);
@ -110,7 +110,7 @@ public:
private:
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 doSendRPC(const json& payload, const std::function<void(json)>& cb, const std::function<void(QString)>& err);
void refreshBalances();
@ -118,8 +118,8 @@ private:
void refreshSentZTrans ();
void refreshReceivedZTrans (QList<QString> zaddresses);
bool processUnspent (const json& reply);
void updateUI (bool anyUnconfirmed);
bool processUnspent (const json& reply);
void updateUI (bool anyUnconfirmed);
void getInfoThenRefresh(bool force);

144
src/sendtab.cpp

@ -11,28 +11,28 @@
using json = nlohmann::json;
void MainWindow::setupSendTab() {
// Create the validator for send to/amount fields
auto amtValidator = new QDoubleValidator(0, 21000000, 8, ui->Amount1);
amtValidator->setNotation(QDoubleValidator::StandardNotation);
ui->Amount1->setValidator(amtValidator);
// Create the validator for send to/amount fields
auto amtValidator = new QDoubleValidator(0, 21000000, 8, ui->Amount1);
amtValidator->setNotation(QDoubleValidator::StandardNotation);
ui->Amount1->setValidator(amtValidator);
setDefaultPayFrom();
// Send button
QObject::connect(ui->sendTransactionButton, &QPushButton::clicked, this, &MainWindow::sendButton);
// Send button
QObject::connect(ui->sendTransactionButton, &QPushButton::clicked, this, &MainWindow::sendButton);
// Cancel Button
QObject::connect(ui->cancelSendButton, &QPushButton::clicked, this, &MainWindow::cancelButton);
// Cancel Button
QObject::connect(ui->cancelSendButton, &QPushButton::clicked, this, &MainWindow::cancelButton);
// Input Combobox current text changed
QObject::connect(ui->inputsCombo, QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::inputComboTextChanged);
// Input Combobox current text changed
QObject::connect(ui->inputsCombo, QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::inputComboTextChanged);
// Hook up add address button click
QObject::connect(ui->addAddressButton, &QPushButton::clicked, this, &MainWindow::addAddressSection);
// Hook up add address button click
QObject::connect(ui->addAddressButton, &QPushButton::clicked, this, &MainWindow::addAddressSection);
// Max available Checkbox
QObject::connect(ui->Max1, &QCheckBox::stateChanged, this, &MainWindow::maxAmountChecked);
// Max available Checkbox
QObject::connect(ui->Max1, &QCheckBox::stateChanged, this, &MainWindow::maxAmountChecked);
// The first Memo button
QObject::connect(ui->MemoBtn1, &QPushButton::clicked, [=] () {
@ -50,10 +50,10 @@ void MainWindow::setupSendTab() {
this->amountChanged(1, text);
});
// Font for the first Memo label
QFont f = ui->Address1->font();
f.setPointSize(f.pointSize() - 1);
ui->MemoTxt1->setFont(f);
// Font for the first Memo label
QFont f = ui->Address1->font();
f.setPointSize(f.pointSize() - 1);
ui->MemoTxt1->setFont(f);
// Set up focus enter to set fees
QObject::connect(ui->tabWidget, &QTabWidget::currentChanged, [=] (int pos) {
@ -287,14 +287,14 @@ void MainWindow::removeExtraAddresses() {
void MainWindow::maxAmountChecked(int checked) {
if (checked == Qt::Checked) {
ui->Amount1->setReadOnly(true);
if (rpc->getAllBalances() == nullptr) return;
if (rpc->getAllBalances() == nullptr) return;
// Calculate maximum amount
double sumAllAmounts = 0.0;
// Calculate all other amounts
int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that
// Start counting the sum skipping the first one, because the MAX button is on the first one, and we don't
// want to include it in the sum.
// Start counting the sum skipping the first one, because the MAX button is on the first one, and we don't
// want to include it in the sum.
for (int i=1; i < totalItems; i++) {
auto amt = ui->sendToWidgets->findChild<QLineEdit*>(QString("Amount") % QString::number(i+1));
sumAllAmounts += amt->text().toDouble();
@ -335,32 +335,32 @@ Tx MainWindow::createTxFromSendPage() {
bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
auto fnSplitAddressForWrap = [=] (const QString& a) -> QString {
if (!a.startsWith("z")) return a;
if (!a.startsWith("z")) return a;
auto half = a.length() / 2;
auto splitted = a.left(half) + "\n" + a.right(a.length() - half);
return splitted;
};
auto half = a.length() / 2;
auto splitted = a.left(half) + "\n" + a.right(a.length() - half);
return splitted;
};
// Show a confirmation dialog
QDialog d(this);
Ui_confirm confirm;
confirm.setupUi(&d);
// Show a confirmation dialog
QDialog d(this);
Ui_confirm confirm;
confirm.setupUi(&d);
// Remove all existing address/amt qlabels on the confirm dialog.
int totalConfirmAddrItems = confirm.sendToAddrs->children().size();
// Remove all existing address/amt qlabels on the confirm dialog.
int totalConfirmAddrItems = confirm.sendToAddrs->children().size();
for (int i = 0; i < totalConfirmAddrItems / 3; i++) {
auto addr = confirm.sendToAddrs->findChild<QLabel*>(QString("Addr") % QString::number(i+1));
auto amt = confirm.sendToAddrs->findChild<QLabel*>(QString("Amt") % QString::number(i+1));
auto addr = confirm.sendToAddrs->findChild<QLabel*>(QString("Addr") % QString::number(i+1));
auto amt = confirm.sendToAddrs->findChild<QLabel*>(QString("Amt") % QString::number(i+1));
auto memo = confirm.sendToAddrs->findChild<QLabel*>(QString("Memo") % QString::number(i+1));
auto amtUSD = confirm.sendToAddrs->findChild<QLabel*>(QString("AmtUSD") % QString::number(i+1));
delete memo;
delete addr;
delete amt;
delete addr;
delete amt;
delete amtUSD;
}
}
// Remove the fee labels
delete confirm.sendToAddrs->findChild<QLabel*>("labelMinerFee");
@ -371,25 +371,25 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
delete confirm.sendToAddrs->findChild<QLabel*>("devFee");
delete confirm.sendToAddrs->findChild<QLabel*>("devFeeUSD");
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
for (int i=0; i < tx.toAddrs.size(); i++) {
auto toAddr = tx.toAddrs[i];
// Add new Address widgets instead of the same one.
{
// Add new Address widgets instead of the same one.
{
// Address
auto Addr = new QLabel(confirm.sendToAddrs);
Addr->setObjectName(QString("Addr") % QString::number(i + 1));
Addr->setWordWrap(true);
Addr->setText(fnSplitAddressForWrap(toAddr.addr));
confirm.gridLayout->addWidget(Addr, i*2, 0, 1, 1);
auto Addr = new QLabel(confirm.sendToAddrs);
Addr->setObjectName(QString("Addr") % QString::number(i + 1));
Addr->setWordWrap(true);
Addr->setText(fnSplitAddressForWrap(toAddr.addr));
confirm.gridLayout->addWidget(Addr, i*2, 0, 1, 1);
// Amount (ZEC)
auto Amt = new QLabel(confirm.sendToAddrs);
Amt->setObjectName(QString("Amt") % QString::number(i + 1));
Amt->setText(Settings::getInstance()->getZECDisplayFormat(toAddr.amount));
Amt->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
confirm.gridLayout->addWidget(Amt, i*2, 1, 1, 1);
auto Amt = new QLabel(confirm.sendToAddrs);
Amt->setObjectName(QString("Amt") % QString::number(i + 1));
Amt->setText(Settings::getInstance()->getZECDisplayFormat(toAddr.amount));
Amt->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
confirm.gridLayout->addWidget(Amt, i*2, 1, 1, 1);
// Amount (USD)
auto AmtUSD = new QLabel(confirm.sendToAddrs);
@ -410,7 +410,7 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
confirm.gridLayout->addWidget(Memo, (i*2)+1, 0, 1, 3);
}
}
}
}
// Add two rows for fees
@ -423,16 +423,16 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
labelMinerFee->setText("Miner Fee");
auto minerFee = new QLabel(confirm.sendToAddrs);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
minerFee->setSizePolicy(sizePolicy);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
minerFee->setSizePolicy(sizePolicy);
minerFee->setObjectName(QStringLiteral("minerFee"));
minerFee->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
confirm.gridLayout->addWidget(minerFee, i, 1, 1, 1);
minerFee->setText(Settings::getInstance()->getZECDisplayFormat(tx.fee));
auto minerFeeUSD = new QLabel(confirm.sendToAddrs);
QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred);
minerFeeUSD->setSizePolicy(sizePolicy1);
QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred);
minerFeeUSD->setSizePolicy(sizePolicy1);
minerFeeUSD->setObjectName(QStringLiteral("minerFeeUSD"));
minerFeeUSD->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
confirm.gridLayout->addWidget(minerFeeUSD, i, 2, 1, 1);
@ -451,7 +451,7 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
fee ->setText(Settings::getInstance()->getZECDisplayFormat(Utils::getDevFee()));
auto devFeeUSD = new QLabel(confirm.sendToAddrs);
devFeeUSD->setSizePolicy(sizePolicy1);
devFeeUSD->setSizePolicy(sizePolicy1);
devFeeUSD->setObjectName(QStringLiteral("devFeeUSD"));
devFeeUSD->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
confirm.gridLayout->addWidget(devFeeUSD, i+1, 2, 1, 1);
@ -459,17 +459,17 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
}
}
// And FromAddress in the confirm dialog
confirm.sendFrom->setText(fnSplitAddressForWrap(tx.fromAddr));
// And FromAddress in the confirm dialog
confirm.sendFrom->setText(fnSplitAddressForWrap(tx.fromAddr));
// Show the dialog and submit it if the user confirms
if (d.exec() == QDialog::Accepted) {
// Then delete the additional fields from the sendTo tab
removeExtraAddresses();
// Show the dialog and submit it if the user confirms
if (d.exec() == QDialog::Accepted) {
// Then delete the additional fields from the sendTo tab
removeExtraAddresses();
return true;
} else {
} else {
return false;
}
}
}
// Send button clicked
@ -500,14 +500,14 @@ void MainWindow::sendButton() {
std::cout << std::setw(2) << params << std::endl;
// And send the Tx
rpc->sendZTransaction(params, [=](const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
ui->statusBar->showMessage("Computing Tx: " % opid);
rpc->sendZTransaction(params, [=](const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
ui->statusBar->showMessage("Computing Tx: " % opid);
// And then start monitoring the transaction
rpc->addNewTxToWatch(tx, opid);
});
}
});
}
}
QString MainWindow::doSendTxValidations(Tx tx) {
@ -535,7 +535,7 @@ QString MainWindow::doSendTxValidations(Tx tx) {
}
void MainWindow::cancelButton() {
removeExtraAddresses();
removeExtraAddresses();
// Back to the balances tab
ui->tabWidget->setCurrentIndex(0);
}

10
src/senttxstore.cpp

@ -41,8 +41,8 @@ QList<TransactionItem> SentTxStore::readSentTxFile() {
TransactionItem t{"send", (qint64)sentTx["datetime"].toVariant().toLongLong(),
sentTx["address"].toString(),
sentTx["txid"].toString(),
sentTx["amount"].toDouble() + sentTx["fee"].toDouble(),
0, sentTx["from"].toString(), ""};
sentTx["amount"].toDouble() + sentTx["fee"].toDouble(),
0, sentTx["from"].toString(), ""};
items.push_back(t);
}
@ -50,9 +50,9 @@ QList<TransactionItem> SentTxStore::readSentTxFile() {
}
void SentTxStore::addToSentTx(Tx tx, QString txid) {
// Save transactions only if the settings are allowed
if (!Settings::getInstance()->getSaveZtxs())
return;
// Save transactions only if the settings are allowed
if (!Settings::getInstance()->getSaveZtxs())
return;
// Also, only store outgoing Txs where the from address is a z-Addr. Else, regular zcashd
// stores it just fine

222
src/settings.cpp

@ -6,59 +6,59 @@
Settings* Settings::instance = nullptr;
Settings::~Settings() {
delete defaults;
delete zcashconf;
delete uisettings;
delete defaults;
delete zcashconf;
delete uisettings;
}
bool Settings::getSaveZtxs() {
// Load from the QT Settings.
return QSettings().value("options/savesenttx", true).toBool();
// Load from the QT Settings.
return QSettings().value("options/savesenttx", true).toBool();
}
void Settings::setSaveZtxs(bool save) {
QSettings().setValue("options/savesenttx", save);
QSettings().setValue("options/savesenttx", save);
}
Settings* Settings::init() {
if (instance == nullptr)
instance = new Settings();
Settings* Settings::init() {
if (instance == nullptr)
instance = new Settings();
// There are 3 possible configurations
// 1. The defaults
instance->defaults = new Config{ "127.0.0.1", "8232", "", "" };
// 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();
// 2. From the UI settings
auto settingsFound = instance->loadFromSettings();
// 3. From the zcash.conf file
auto confFound = instance->loadFromFile();
// 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;
}
// 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;
}
Settings* Settings::getInstance() {
return instance;
return instance;
}
QString Settings::getHost() {
return currentConfig->host;
return currentConfig->host;
}
QString Settings::getPort() {
return currentConfig->port;
return currentConfig->port;
}
@ -67,155 +67,155 @@ QString Settings::getUsernamePassword() {
}
bool Settings::loadFromSettings() {
delete uisettings;
delete uisettings;
// 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();
// 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();
uisettings = new Config{host, port, username, password};
uisettings = new Config{host, port, username, password};
return !username.isEmpty();
}
void Settings::saveSettings(const QString& host, const QString& port, const QString& username, const QString& password) {
QSettings s;
QSettings s;
s.setValue("connection/host", host);
s.setValue("connection/port", port);
s.setValue("connection/rpcuser", username);
s.setValue("connection/rpcpassword", password);
s.setValue("connection/host", host);
s.setValue("connection/port", port);
s.setValue("connection/rpcuser", username);
s.setValue("connection/rpcpassword", password);
s.sync();
s.sync();
// re-init to load correct settings
init();
// re-init to load correct settings
init();
}
bool Settings::loadFromFile() {
delete zcashconf;
delete zcashconf;
#ifdef Q_OS_LINUX
confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".zcash/zcash.conf");
confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".zcash/zcash.conf");
#elif defined(Q_OS_DARWIN)
confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "/Library/Application Support/Zcash/zcash.conf");
confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "/Library/Application Support/Zcash/zcash.conf");
#else
confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Zcash/zcash.conf");
confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Zcash/zcash.conf");
#endif
confLocation = QDir::cleanPath(confLocation);
confLocation = QDir::cleanPath(confLocation);
if (confLocation.isNull()) {
// No zcash file, just return with nothing
return false;
}
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;
}
QFile file(confLocation);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << file.errorString();
return false;
}
QTextStream in(&file);
QTextStream in(&file);
zcashconf = new Config();
zcashconf->host = defaults->host;
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();
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 (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;
// 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();
file.close();
return true;
return true;
}
bool Settings::isTestnet() {
return _isTestnet;
return _isTestnet;
}
void Settings::setTestnet(bool isTestnet) {
this->_isTestnet = isTestnet;
this->_isTestnet = isTestnet;
}
bool Settings::isSaplingAddress(QString addr) {
return ( isTestnet() && addr.startsWith("ztestsapling")) ||
(!isTestnet() && addr.startsWith("zs"));
return ( isTestnet() && addr.startsWith("ztestsapling")) ||
(!isTestnet() && addr.startsWith("zs"));
}
bool Settings::isSproutAddress(QString addr) {
return isZAddress(addr) && !isSaplingAddress(addr);
return isZAddress(addr) && !isSaplingAddress(addr);
}
bool Settings::isZAddress(QString addr) {
return addr.startsWith("z");
return addr.startsWith("z");
}
bool Settings::isSyncing() {
return _isSyncing;
return _isSyncing;
}
void Settings::setSyncing(bool syncing) {
this->_isSyncing = syncing;
this->_isSyncing = syncing;
}
int Settings::getBlockNumber() {
return this->_blockNumber;
return this->_blockNumber;
}
void Settings::setBlockNumber(int number) {
this->_blockNumber = number;
this->_blockNumber = number;
}
bool Settings::isSaplingActive() {
return (isTestnet() && getBlockNumber() > 280000) ||
(!isTestnet() && getBlockNumber() > 419200);
return (isTestnet() && getBlockNumber() > 280000) ||
(!isTestnet() && getBlockNumber() > 419200);
}
double Settings::getZECPrice() {
return zecPrice;
return zecPrice;
}
QString Settings::getUSDFormat(double bal) {
if (!isTestnet() && getZECPrice() > 0)
return "$" + QLocale(QLocale::English).toString(bal * getZECPrice(), 'f', 2);
else
return QString();
if (!isTestnet() && getZECPrice() > 0)
return "$" + QLocale(QLocale::English).toString(bal * getZECPrice(), 'f', 2);
else
return QString();
}
QString Settings::getZECDisplayFormat(double bal) {
return QString::number(bal, 'g', 8) % " " % Utils::getTokenName();
return QString::number(bal, 'g', 8) % " " % Utils::getTokenName();
}
QString Settings::getZECUSDDisplayFormat(double bal) {
auto usdFormat = getUSDFormat(bal);
if (!usdFormat.isEmpty())
return getZECDisplayFormat(bal) % " (" % getUSDFormat(bal) % ")";
else
return getZECDisplayFormat(bal);
auto usdFormat = getUSDFormat(bal);
if (!usdFormat.isEmpty())
return getZECDisplayFormat(bal) % " (" % getUSDFormat(bal) % ")";
else
return getZECDisplayFormat(bal);
}

66
src/settings.h

@ -4,10 +4,10 @@
#include "precompiled.h"
struct Config {
QString host;
QString port;
QString rpcuser;
QString rpcpassword;
QString host;
QString port;
QString rpcuser;
QString rpcpassword;
};
class Settings
@ -17,61 +17,61 @@ public:
static Settings* getInstance();
QString getUsernamePassword();
QString getHost();
QString getPort();
QString getHost();
QString getPort();
bool loadFromSettings();
bool loadFromSettings();
bool loadFromFile();
void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password);
void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password);
bool isTestnet();
void setTestnet(bool isTestnet);
bool isSaplingAddress(QString addr);
bool isSproutAddress(QString addr);
bool isZAddress(QString addr);
bool isSaplingAddress(QString addr);
bool isSproutAddress(QString addr);
bool isZAddress(QString addr);
bool isSyncing();
void setSyncing(bool syncing);
bool isSyncing();
void setSyncing(bool syncing);
int getBlockNumber();
void setBlockNumber(int number);
int getBlockNumber();
void setBlockNumber(int number);
bool getSaveZtxs();
void setSaveZtxs(bool save);
bool getSaveZtxs();
void setSaveZtxs(bool save);
bool isSaplingActive();
bool isSaplingActive();
const QString& getZcashdConfLocation() { return confLocation; }
const QString& getZcashdConfLocation() { return confLocation; }
void setZECPrice(double p) { zecPrice = p; }
double getZECPrice();
void setZECPrice(double p) { zecPrice = p; }
double getZECPrice();
QString getUSDFormat (double bal);
QString getZECDisplayFormat (double bal);
QString getZECUSDDisplayFormat (double bal);
QString getUSDFormat (double bal);
QString getZECDisplayFormat (double bal);
QString getZECUSDDisplayFormat (double bal);
private:
// This class can only be accessed through Settings::getInstance()
Settings() = default;
~Settings();
~Settings();
static Settings* instance;
Config* currentConfig;
Config* currentConfig;
Config* defaults = nullptr;
Config* zcashconf = nullptr;
Config* uisettings = nullptr;
Config* defaults = nullptr;
Config* zcashconf = nullptr;
Config* uisettings = nullptr;
QString confLocation;
QString confLocation;
bool _isTestnet = false;
bool _isSyncing = false;
int _blockNumber = 0;
bool _isSyncing = false;
int _blockNumber = 0;
double zecPrice = 0.0;
double zecPrice = 0.0;
};
#endif // SETTINGS_H

546
src/turnstile.cpp

@ -10,18 +10,18 @@
using json = nlohmann::json;
Turnstile::Turnstile(RPC* _rpc, MainWindow* mainwindow) {
this->rpc = _rpc;
this->mainwindow = mainwindow;
this->rpc = _rpc;
this->mainwindow = mainwindow;
}
Turnstile::~Turnstile() {
}
void printPlan(QList<TurnstileMigrationItem> plan) {
for (auto item : plan) {
//qDebug() << item.fromAddr << item.intTAddr
// << item.destAddr << item.amount << item.blockNumber << item.status;
}
for (auto item : plan) {
//qDebug() << item.fromAddr << item.intTAddr
// << item.destAddr << item.amount << item.blockNumber << item.status;
}
}
QString Turnstile::writeableFile() {
@ -39,333 +39,333 @@ QString Turnstile::writeableFile() {
}
void Turnstile::removeFile() {
QFile(writeableFile()).remove();
QFile(writeableFile()).remove();
}
// Data stream write/read methods for migration items
QDataStream &operator<<(QDataStream& ds, const TurnstileMigrationItem& item) {
return ds << QString("v1") << item.fromAddr << item.intTAddr
<< item.destAddr << item.amount << item.blockNumber << item.status;
return ds << QString("v1") << item.fromAddr << item.intTAddr
<< item.destAddr << item.amount << item.blockNumber << item.status;
}
QDataStream &operator>>(QDataStream& ds, TurnstileMigrationItem& item) {
QString version;
return ds >> version >> item.fromAddr >> item.intTAddr
>> item.destAddr >> item.amount >> item.blockNumber >> item.status;
QString version;
return ds >> version >> item.fromAddr >> item.intTAddr
>> item.destAddr >> item.amount >> item.blockNumber >> item.status;
}
void Turnstile::writeMigrationPlan(QList<TurnstileMigrationItem> plan) {
//qDebug() << QString("Writing plan");
printPlan(plan);
QFile file(writeableFile());
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
QDataStream out(&file); // we will serialize the data into the file
out << plan;
file.close();
//qDebug() << QString("Writing plan");
printPlan(plan);
QFile file(writeableFile());
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
QDataStream out(&file); // we will serialize the data into the file
out << plan;
file.close();
}
QList<TurnstileMigrationItem> Turnstile::readMigrationPlan() {
QFile file(writeableFile());
QList<TurnstileMigrationItem> plan;
if (!file.exists()) return plan;
QFile file(writeableFile());
QList<TurnstileMigrationItem> plan;
if (!file.exists()) return plan;
file.open(QIODevice::ReadOnly);
QDataStream in(&file); // read the data serialized from the file
in >> plan;
file.open(QIODevice::ReadOnly);
QDataStream in(&file); // read the data serialized from the file
in >> plan;
file.close();
file.close();
// Sort to see when the next step is.
std::sort(plan.begin(), plan.end(), [&] (auto a, auto b) {
return a.blockNumber < b.blockNumber;
});
// Sort to see when the next step is.
std::sort(plan.begin(), plan.end(), [&] (auto a, auto b) {
return a.blockNumber < b.blockNumber;
});
return plan;
return plan;
}
void Turnstile::planMigration(QString zaddr, QString destAddr, int numsplits, int numBlocks) {
// First, get the balance and split up the amounts
auto bal = rpc->getAllBalances()->value(zaddr);
auto splits = splitAmount(bal, numsplits);
// Then, generate an intermediate t-Address for each part using getBatchRPC
rpc->getBatchRPC<double>(splits,
[=] (double /*unused*/) {
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
{"method", "getnewaddress"},
};
return payload;
},
[=] (QMap<double, json>* newAddrs) {
// First, get the balance and split up the amounts
auto bal = rpc->getAllBalances()->value(zaddr);
auto splits = splitAmount(bal, numsplits);
// Then, generate an intermediate t-Address for each part using getBatchRPC
rpc->getBatchRPC<double>(splits,
[=] (double /*unused*/) {
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
{"method", "getnewaddress"},
};
return payload;
},
[=] (QMap<double, json>* newAddrs) {
// Get block numbers
auto curBlock = Settings::getInstance()->getBlockNumber();
auto blockNumbers = getBlockNumbers(curBlock, curBlock + numBlocks, splits.size());
// Assign the amounts to the addresses.
QList<TurnstileMigrationItem> migItems;
for (int i=0; i < splits.size(); i++) {
auto tAddr = newAddrs->values()[i].get<json::string_t>();
auto item = TurnstileMigrationItem { zaddr, QString::fromStdString(tAddr), destAddr,
blockNumbers[i], splits[i],
TurnstileMigrationItemStatus::NotStarted };
migItems.push_back(item);
}
// The first migration is shifted to the current block, so the user sees something
// happening immediately
if (migItems.size() == 0) {
// Show error and abort
QMessageBox::warning(mainwindow,
"Locked funds", "Could not initiate migration.\nYou either have unconfirmed funds or the balance is too low for an automatic migration.");
return;
}
migItems[0].blockNumber = curBlock;
std::sort(migItems.begin(), migItems.end(), [&] (auto a, auto b) {
return a.blockNumber < b.blockNumber;
});
writeMigrationPlan(migItems);
rpc->refresh(true); // Force refresh, to start the migration immediately
}
);
auto curBlock = Settings::getInstance()->getBlockNumber();
auto blockNumbers = getBlockNumbers(curBlock, curBlock + numBlocks, splits.size());
// Assign the amounts to the addresses.
QList<TurnstileMigrationItem> migItems;
for (int i=0; i < splits.size(); i++) {
auto tAddr = newAddrs->values()[i].get<json::string_t>();
auto item = TurnstileMigrationItem { zaddr, QString::fromStdString(tAddr), destAddr,
blockNumbers[i], splits[i],
TurnstileMigrationItemStatus::NotStarted };
migItems.push_back(item);
}
// The first migration is shifted to the current block, so the user sees something
// happening immediately
if (migItems.size() == 0) {
// Show error and abort
QMessageBox::warning(mainwindow,
"Locked funds", "Could not initiate migration.\nYou either have unconfirmed funds or the balance is too low for an automatic migration.");
return;
}
migItems[0].blockNumber = curBlock;
std::sort(migItems.begin(), migItems.end(), [&] (auto a, auto b) {
return a.blockNumber < b.blockNumber;
});
writeMigrationPlan(migItems);
rpc->refresh(true); // Force refresh, to start the migration immediately
}
);
}
QList<int> Turnstile::getBlockNumbers(int start, int end, int count) {
QList<int> blocks;
// Generate 'count' numbers between [start, end]
for (int i=0; i < count; i++) {
auto blk = (std::rand() % (end - start)) + start;
blocks.push_back(blk);
}
return blocks;
QList<int> blocks;
// Generate 'count' numbers between [start, end]
for (int i=0; i < count; i++) {
auto blk = (std::rand() % (end - start)) + start;
blocks.push_back(blk);
}
return blocks;
}
// Need at least 0.0005 ZEC for this
// Need at least 0.0005 ZEC for this
double Turnstile::minMigrationAmount = 0.0005;
QList<double> Turnstile::splitAmount(double amount, int parts) {
QList<double> amounts;
if (amount < minMigrationAmount)
return amounts;
fillAmounts(amounts, amount, parts);
//qDebug() << amounts;
// Ensure they all add up!
double sumofparts = 0;
for (auto a : amounts) {
sumofparts += a;
}
// Add the Tx fees
sumofparts += amounts.size() * Utils::getMinerFee();
//qDebug() << QString::number(sumofparts, 'f', 8) << QString::number(amount, 'f', 8);
//Q_ASSERT(QString::number(sumofparts, 'f', 8) == QString::number(amount, 'f', 8));
return amounts;
QList<double> amounts;
if (amount < minMigrationAmount)
return amounts;
fillAmounts(amounts, amount, parts);
//qDebug() << amounts;
// Ensure they all add up!
double sumofparts = 0;
for (auto a : amounts) {
sumofparts += a;
}
// Add the Tx fees
sumofparts += amounts.size() * Utils::getMinerFee();
//qDebug() << QString::number(sumofparts, 'f', 8) << QString::number(amount, 'f', 8);
//Q_ASSERT(QString::number(sumofparts, 'f', 8) == QString::number(amount, 'f', 8));
return amounts;
}
void Turnstile::fillAmounts(QList<double>& amounts, double amount, int count) {
if (count == 1 || amount < 0.01) {
// Also account for the fees needed to send all these transactions
auto actual = amount - (Utils::getMinerFee() * (amounts.size() + 1));
if (count == 1 || amount < 0.01) {
// Also account for the fees needed to send all these transactions
auto actual = amount - (Utils::getMinerFee() * (amounts.size() + 1));
amounts.push_back(actual);
return;
}
amounts.push_back(actual);
return;
}
// Get a random amount off the amount (between half and full) and call recursively.
// Multiply by hundered, because we'll operate on 0.01 ZEC minimum. We'll divide by 100 later
double curAmount = std::rand() % (int)std::floor(amount * 100);
// Get a random amount off the amount (between half and full) and call recursively.
// Multiply by hundered, because we'll operate on 0.01 ZEC minimum. We'll divide by 100 later
double curAmount = std::rand() % (int)std::floor(amount * 100);
// Try to round it off
auto places = (int)std::floor(std::log10(curAmount));
if (places > 0) {
auto a = std::pow(10, places);
curAmount = std::floor(curAmount / a) * a;
}
// Try to round it off
auto places = (int)std::floor(std::log10(curAmount));
if (places > 0) {
auto a = std::pow(10, places);
curAmount = std::floor(curAmount / a) * a;
}
// And divide by 100
curAmount = curAmount / 100;
// And divide by 100
curAmount = curAmount / 100;
if (curAmount > 0)
amounts.push_back(curAmount);
if (curAmount > 0)
amounts.push_back(curAmount);
fillAmounts(amounts, amount - curAmount, count - 1);
fillAmounts(amounts, amount - curAmount, count - 1);
}
QList<TurnstileMigrationItem>::Iterator
Turnstile::getNextStep(QList<TurnstileMigrationItem>& plan) {
// Get to the next unexecuted step
auto fnIsEligibleItem = [&] (auto item) {
return item.status == TurnstileMigrationItemStatus::NotStarted ||
item.status == TurnstileMigrationItemStatus::SentToT;
};
// Find the next step
auto nextStep = std::find_if(plan.begin(), plan.end(), fnIsEligibleItem);
return nextStep;
// Get to the next unexecuted step
auto fnIsEligibleItem = [&] (auto item) {
return item.status == TurnstileMigrationItemStatus::NotStarted ||
item.status == TurnstileMigrationItemStatus::SentToT;
};
// Find the next step
auto nextStep = std::find_if(plan.begin(), plan.end(), fnIsEligibleItem);
return nextStep;
}
bool Turnstile::isMigrationPresent() {
auto plan = readMigrationPlan();
if (plan.isEmpty()) return false;
auto plan = readMigrationPlan();
if (plan.isEmpty()) return false;
return true;
return true;
}
ProgressReport Turnstile::getPlanProgress() {
auto plan = readMigrationPlan();
auto plan = readMigrationPlan();
auto nextStep = getNextStep(plan);
auto nextStep = getNextStep(plan);
auto step = std::distance(plan.begin(), nextStep) * 2; // 2 steps per item
if (nextStep != plan.end() &&
nextStep->status == TurnstileMigrationItemStatus::SentToT)
step++;
auto total = plan.size();
auto step = std::distance(plan.begin(), nextStep) * 2; // 2 steps per item
if (nextStep != plan.end() &&
nextStep->status == TurnstileMigrationItemStatus::SentToT)
step++;
auto total = plan.size();
auto nextBlock = nextStep == plan.end() ? 0 : nextStep->blockNumber;
auto nextBlock = nextStep == plan.end() ? 0 : nextStep->blockNumber;
bool hasErrors = std::find_if(plan.begin(), plan.end(), [=] (auto i) {
return i.status == TurnstileMigrationItemStatus::NotEnoughBalance ||
i.status == TurnstileMigrationItemStatus::UnknownError;
}) != plan.end();
bool hasErrors = std::find_if(plan.begin(), plan.end(), [=] (auto i) {
return i.status == TurnstileMigrationItemStatus::NotEnoughBalance ||
i.status == TurnstileMigrationItemStatus::UnknownError;
}) != plan.end();
auto stepData = (nextStep == plan.end() ? std::prev(nextStep) : nextStep);
auto stepData = (nextStep == plan.end() ? std::prev(nextStep) : nextStep);
return ProgressReport{(int)step, total*2, nextBlock, hasErrors, stepData->fromAddr, stepData->destAddr, stepData->intTAddr};
return ProgressReport{(int)step, total*2, nextBlock, hasErrors, stepData->fromAddr, stepData->destAddr, stepData->intTAddr};
}
void Turnstile::executeMigrationStep() {
auto plan = readMigrationPlan();
//qDebug() << QString("Executing step");
printPlan(plan);
// Get to the next unexecuted step
auto fnIsEligibleItem = [&] (auto item) {
return item.status == TurnstileMigrationItemStatus::NotStarted ||
item.status == TurnstileMigrationItemStatus::SentToT;
};
// Fn to find if there are any unconfirmed funds for this address.
auto fnHasUnconfirmed = [=] (QString addr) {
auto utxoset = rpc->getUTXOs();
return std::find_if(utxoset->begin(), utxoset->end(), [=] (auto utxo) {
return utxo.address == addr && utxo.confirmations == 0 && utxo.spendable;
}) != utxoset->end();
};
// Find the next step
auto nextStep = std::find_if(plan.begin(), plan.end(), fnIsEligibleItem);
if (nextStep == plan.end())
return; // Nothing to do
if (nextStep->blockNumber > Settings::getInstance()->getBlockNumber())
return;
// Is this the last step for this address?
auto lastStep = std::find_if(std::next(nextStep), plan.end(), fnIsEligibleItem) == plan.end();
// Execute this step
if (nextStep->status == TurnstileMigrationItemStatus::NotStarted) {
// Does this z addr have enough balance?
if (fnHasUnconfirmed(nextStep->fromAddr)) {
//qDebug() << QString("unconfirmed, waiting");
return;
}
auto balance = rpc->getAllBalances()->value(nextStep->fromAddr);
if (nextStep->amount > balance) {
qDebug() << "Not enough balance!";
nextStep->status = TurnstileMigrationItemStatus::NotEnoughBalance;
writeMigrationPlan(plan);
return;
}
auto to = ToFields{ nextStep->intTAddr, nextStep->amount, "", "" };
// If this is the last step, then send the remaining amount instead of the actual amount.
if (lastStep) {
auto remainingAmount = balance - Utils::getMinerFee();
if (remainingAmount > 0) {
to.amount = remainingAmount;
}
}
// Create the Tx
auto tx = Tx{ nextStep->fromAddr, { to }, Utils::getMinerFee() };
// And send it
doSendTx(tx, [=] () {
// Update status and write plan to disk
nextStep->status = TurnstileMigrationItemStatus::SentToT;
writeMigrationPlan(plan);
});
} else if (nextStep->status == TurnstileMigrationItemStatus::SentToT) {
// First thing to do is check to see if the funds are confirmed.
// We'll check both the original sprout address and the intermediate T addr for safety.
if (fnHasUnconfirmed(nextStep->intTAddr) || fnHasUnconfirmed(nextStep->fromAddr)) {
//qDebug() << QString("unconfirmed, waiting");
return;
}
if (!rpc->getAllBalances()->keys().contains(nextStep->intTAddr)) {
qDebug() << QString("The intermediate Taddress doesn't have balance, even though it is confirmed");
return;
}
// Send it to the final destination address.
auto bal = rpc->getAllBalances()->value(nextStep->intTAddr);
auto sendAmt = bal - Utils::getMinerFee();
if (sendAmt < 0) {
qDebug() << "Not enough balance!." << bal << ":" << sendAmt;
nextStep->status = TurnstileMigrationItemStatus::NotEnoughBalance;
writeMigrationPlan(plan);
return;
}
QList<ToFields> to = { ToFields{ nextStep->destAddr, sendAmt, "", "" } };
// Create the Tx
auto tx = Tx{ nextStep->intTAddr, to, Utils::getMinerFee() };
// And send it
doSendTx(tx, [=] () {
// Update status and write plan to disk
nextStep->status = TurnstileMigrationItemStatus::SentToZS;
writeMigrationPlan(plan);
});
}
auto plan = readMigrationPlan();
//qDebug() << QString("Executing step");
printPlan(plan);
// Get to the next unexecuted step
auto fnIsEligibleItem = [&] (auto item) {
return item.status == TurnstileMigrationItemStatus::NotStarted ||
item.status == TurnstileMigrationItemStatus::SentToT;
};
// Fn to find if there are any unconfirmed funds for this address.
auto fnHasUnconfirmed = [=] (QString addr) {
auto utxoset = rpc->getUTXOs();
return std::find_if(utxoset->begin(), utxoset->end(), [=] (auto utxo) {
return utxo.address == addr && utxo.confirmations == 0 && utxo.spendable;
}) != utxoset->end();
};
// Find the next step
auto nextStep = std::find_if(plan.begin(), plan.end(), fnIsEligibleItem);
if (nextStep == plan.end())
return; // Nothing to do
if (nextStep->blockNumber > Settings::getInstance()->getBlockNumber())
return;
// Is this the last step for this address?
auto lastStep = std::find_if(std::next(nextStep), plan.end(), fnIsEligibleItem) == plan.end();
// Execute this step
if (nextStep->status == TurnstileMigrationItemStatus::NotStarted) {
// Does this z addr have enough balance?
if (fnHasUnconfirmed(nextStep->fromAddr)) {
//qDebug() << QString("unconfirmed, waiting");
return;
}
auto balance = rpc->getAllBalances()->value(nextStep->fromAddr);
if (nextStep->amount > balance) {
qDebug() << "Not enough balance!";
nextStep->status = TurnstileMigrationItemStatus::NotEnoughBalance;
writeMigrationPlan(plan);
return;
}
auto to = ToFields{ nextStep->intTAddr, nextStep->amount, "", "" };
// If this is the last step, then send the remaining amount instead of the actual amount.
if (lastStep) {
auto remainingAmount = balance - Utils::getMinerFee();
if (remainingAmount > 0) {
to.amount = remainingAmount;
}
}
// Create the Tx
auto tx = Tx{ nextStep->fromAddr, { to }, Utils::getMinerFee() };
// And send it
doSendTx(tx, [=] () {
// Update status and write plan to disk
nextStep->status = TurnstileMigrationItemStatus::SentToT;
writeMigrationPlan(plan);
});
} else if (nextStep->status == TurnstileMigrationItemStatus::SentToT) {
// First thing to do is check to see if the funds are confirmed.
// We'll check both the original sprout address and the intermediate T addr for safety.
if (fnHasUnconfirmed(nextStep->intTAddr) || fnHasUnconfirmed(nextStep->fromAddr)) {
//qDebug() << QString("unconfirmed, waiting");
return;
}
if (!rpc->getAllBalances()->keys().contains(nextStep->intTAddr)) {
qDebug() << QString("The intermediate Taddress doesn't have balance, even though it is confirmed");
return;
}
// Send it to the final destination address.
auto bal = rpc->getAllBalances()->value(nextStep->intTAddr);
auto sendAmt = bal - Utils::getMinerFee();
if (sendAmt < 0) {
qDebug() << "Not enough balance!." << bal << ":" << sendAmt;
nextStep->status = TurnstileMigrationItemStatus::NotEnoughBalance;
writeMigrationPlan(plan);
return;
}
QList<ToFields> to = { ToFields{ nextStep->destAddr, sendAmt, "", "" } };
// Create the Tx
auto tx = Tx{ nextStep->intTAddr, to, Utils::getMinerFee() };
// And send it
doSendTx(tx, [=] () {
// Update status and write plan to disk
nextStep->status = TurnstileMigrationItemStatus::SentToZS;
writeMigrationPlan(plan);
});
}
}
void Turnstile::doSendTx(Tx tx, std::function<void(void)> cb) {
json params = json::array();
rpc->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
rpc->sendZTransaction(params, [=] (const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
//qDebug() << opid;
mainwindow->ui->statusBar->showMessage("Computing Tx: " % opid);
// And then start monitoring the transaction
rpc->addNewTxToWatch(tx, opid);
cb();
});
json params = json::array();
rpc->fillTxJsonParams(params, tx);
std::cout << std::setw(2) << params << std::endl;
rpc->sendZTransaction(params, [=] (const json& reply) {
QString opid = QString::fromStdString(reply.get<json::string_t>());
//qDebug() << opid;
mainwindow->ui->statusBar->showMessage("Computing Tx: " % opid);
// And then start monitoring the transaction
rpc->addNewTxToWatch(tx, opid);
cb();
});
}

74
src/turnstile.h

@ -9,62 +9,62 @@ struct Tx;
struct TurnstileMigrationItem {
QString fromAddr;
QString intTAddr;
QString destAddr;
int blockNumber;
double amount;
int status;
QString fromAddr;
QString intTAddr;
QString destAddr;
int blockNumber;
double amount;
int status;
};
enum TurnstileMigrationItemStatus {
NotStarted = 0,
SentToT,
SentToZS,
NotEnoughBalance,
UnknownError
NotStarted = 0,
SentToT,
SentToZS,
NotEnoughBalance,
UnknownError
};
struct ProgressReport {
int step;
int totalSteps;
int nextBlock;
bool hasErrors;
QString from;
QString to;
QString via;
int step;
int totalSteps;
int nextBlock;
bool hasErrors;
QString from;
QString to;
QString via;
};
class Turnstile
{
public:
Turnstile(RPC* _rpc, MainWindow* mainwindow);
~Turnstile();
Turnstile(RPC* _rpc, MainWindow* mainwindow);
~Turnstile();
void planMigration(QString zaddr, QString destAddr, int splits, int numBlocks);
QList<double> splitAmount(double amount, int parts);
void fillAmounts(QList<double>& amounts, double amount, int count);
void planMigration(QString zaddr, QString destAddr, int splits, int numBlocks);
QList<double> splitAmount(double amount, int parts);
void fillAmounts(QList<double>& amounts, double amount, int count);
QList<TurnstileMigrationItem> readMigrationPlan();
void writeMigrationPlan(QList<TurnstileMigrationItem> plan);
void removeFile();
void executeMigrationStep();
ProgressReport getPlanProgress();
bool isMigrationPresent();
QList<TurnstileMigrationItem> readMigrationPlan();
void writeMigrationPlan(QList<TurnstileMigrationItem> plan);
void removeFile();
void executeMigrationStep();
ProgressReport getPlanProgress();
bool isMigrationPresent();
static double minMigrationAmount;
static double minMigrationAmount;
private:
QList<int> getBlockNumbers(int start, int end, int count);
QString writeableFile();
QList<int> getBlockNumbers(int start, int end, int count);
QString writeableFile();
void doSendTx(Tx tx, std::function<void(void)> cb);
void doSendTx(Tx tx, std::function<void(void)> cb);
QList<TurnstileMigrationItem>::Iterator getNextStep(QList<TurnstileMigrationItem>& plan);
QList<TurnstileMigrationItem>::Iterator getNextStep(QList<TurnstileMigrationItem>& plan);
RPC* rpc;
MainWindow* mainwindow;
RPC* rpc;
MainWindow* mainwindow;
};
#endif

26
src/txtablemodel.cpp

@ -73,19 +73,19 @@ void TxTableModel::updateAllData() {
{
// Align column 4 (amount) right
if (role == Qt::TextAlignmentRole && index.column() == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter);
if (role == Qt::ForegroundRole) {
if (role == Qt::ForegroundRole) {
if (modeldata->at(index.row()).confirmations == 0) {
QBrush b;
b.setColor(Qt::red);
return b;
}
}
// Else, just return the default brush
// Else, just return the default brush
QBrush b;
b.setColor(Qt::black);
return b;
}
b.setColor(Qt::black);
return b;
}
auto dat = modeldata->at(index.row());
if (role == Qt::DisplayRole) {
@ -98,7 +98,7 @@ void TxTableModel::updateAllData() {
else
return addr;
}
case 2: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString();
case 2: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString();
case 3: return Settings::getInstance()->getZECDisplayFormat(modeldata->at(index.row()).amount);
}
}
@ -140,11 +140,11 @@ void TxTableModel::updateAllData() {
{
if (role == Qt::TextAlignmentRole && section == 3) return QVariant(Qt::AlignRight | Qt::AlignVCenter);
if (role == Qt::FontRole) {
QFont f;
f.setBold(true);
return f;
}
if (role == Qt::FontRole) {
QFont f;
f.setBold(true);
return f;
}
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
return headers.at(section);

26
src/utils.h

@ -9,22 +9,22 @@ struct Tx;
class Utils
{
public:
static const QString txidStatusMessage;
static const QString txidStatusMessage;
static const QString getTokenName();
static const QString getDevSproutAddr();
static const QString getDevAddr(Tx tx);
static const QString getDonationAddr(bool sapling);
static const QString getTokenName();
static const QString getDevSproutAddr();
static const QString getDevAddr(Tx tx);
static const QString getDonationAddr(bool sapling);
static double getMinerFee();
static double getDevFee();
static double getTotalFee();
static double getMinerFee();
static double getDevFee();
static double getTotalFee();
static const int updateSpeed = 20 * 1000; // 20 sec
static const int quickUpdateSpeed = 5 * 1000; // 5 sec
static const int priceRefreshSpeed = 60 * 60 * 1000; // 1 hr
private:
Utils() = delete;
static const int updateSpeed = 20 * 1000; // 20 sec
static const int quickUpdateSpeed = 5 * 1000; // 5 sec
static const int priceRefreshSpeed = 60 * 60 * 1000; // 1 hr
private:
Utils() = delete;
};
#endif // UTILS_H
Loading…
Cancel
Save