diff --git a/src/balancestablemodel.cpp b/src/balancestablemodel.cpp index f268fb1..549dddd 100644 --- a/src/balancestablemodel.cpp +++ b/src/balancestablemodel.cpp @@ -3,128 +3,128 @@ #include "utils.h" BalancesTableModel::BalancesTableModel(QObject *parent) - : QAbstractTableModel(parent) { + : QAbstractTableModel(parent) { } void BalancesTableModel::setNewData(const QMap* balances, - const QList* outputs) -{ - loading = false; - - int currentRows = rowCount(QModelIndex()); - // Copy over the utxos for our use - delete utxos; - utxos = new QList(); - // This is a QList deep copy. - *utxos = *outputs; - - // Process the address balances into a list - delete modeldata; - modeldata = new QList>(); - 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* outputs) +{ + loading = false; + + int currentRows = rowCount(QModelIndex()); + // Copy over the utxos for our use + delete utxos; + utxos = new QList(); + // This is a QList deep copy. + *utxos = *outputs; + + // Process the address balances into a list + delete modeldata; + modeldata = new QList>(); + 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(); } diff --git a/src/main.cpp b/src/main.cpp index c86c2af..6f1c5c1 100644 --- a/src/main.cpp +++ b/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(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6648423..d5bd7e8 100644 --- a/src/mainwindow.cpp +++ b/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", [=] () { diff --git a/src/mainwindow.h b/src/mainwindow.h index a604999..8d61f4c 100644 --- a/src/mainwindow.h +++ b/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* keys); - void restoreSavedStates(); + void restoreSavedStates(); RPC* rpc; diff --git a/src/rpc.cpp b/src/rpc.cpp index 1ae555d..87d0f89 100644 --- a/src/rpc.cpp +++ b/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& cb) { } void RPC::doSendRPC(const json& payload, const std::function& cb, const std::function& 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& 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& 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= and rpcpassword= 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= and rpcpassword= 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()); - }; + // Testnet? + if (!reply["testnet"].is_null()) { + Settings::getInstance()->setTestnet(reply["testnet"].get()); + }; - // 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(); @@ -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(); bool isSyncing = progress < 0.999; // 99.9% int blockNumber = reply["blocks"].get(); @@ -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()) { - QString qsAddr = QString::fromStdString(it["address"]); - auto confirmations = it["confirmations"].get(); - if (confirmations == 0) { - anyUnconfirmed = true; - } - - utxos->push_back( - UnspentOutput{ qsAddr, QString::fromStdString(it["txid"]), + bool anyUnconfirmed = false; + for (auto& it : reply.get()) { + QString qsAddr = QString::fromStdString(it["address"]); + auto confirmations = it["confirmations"].get(); + if (confirmations == 0) { + anyUnconfirmed = true; + } + + utxos->push_back( + UnspentOutput{ qsAddr, QString::fromStdString(it["txid"]), QString::number(it["amount"].get(), 'g', 8), (int)confirmations, it["spendable"].get() }); - (*allBalances)[qsAddr] = (*allBalances)[qsAddr] + it["amount"].get(); - } - return anyUnconfirmed; + (*allBalances)[qsAddr] = (*allBalances)[qsAddr] + it["amount"].get(); + } + return anyUnconfirmed; }; void RPC::refreshBalances() { @@ -648,10 +648,10 @@ void RPC::refreshTransactions() { QList txdata; for (auto& it : reply.get()) { - double fee = 0; - if (!it["fee"].is_null()) { - fee = it["fee"].get(); - } + double fee = 0; + if (!it["fee"].is_null()) { + fee = it["fee"].get(); + } 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); diff --git a/src/rpc.h b/src/rpc.h index d875b8c..51eee5f 100644 --- a/src/rpc.h +++ b/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* getUTXOs() { return utxos; } const QMap* getAllBalances() { return allBalances; } - void reloadConnectionInfo(); + void reloadConnectionInfo(); void newZaddr(bool sapling, const std::function& cb); void newTaddr(const std::function& cb); @@ -110,7 +110,7 @@ public: private: void doRPC (const json& payload, const std::function& cb); void doSendRPC (const json& payload, const std::function& cb); - void doSendRPC(const json& payload, const std::function& cb, const std::function& err); + void doSendRPC(const json& payload, const std::function& cb, const std::function& err); void refreshBalances(); @@ -118,8 +118,8 @@ private: void refreshSentZTrans (); void refreshReceivedZTrans (QList zaddresses); - bool processUnspent (const json& reply); - void updateUI (bool anyUnconfirmed); + bool processUnspent (const json& reply); + void updateUI (bool anyUnconfirmed); void getInfoThenRefresh(bool force); diff --git a/src/sendtab.cpp b/src/sendtab.cpp index 5ffaea0..1d68dcd 100644 --- a/src/sendtab.cpp +++ b/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::of(&QComboBox::currentIndexChanged), - this, &MainWindow::inputComboTextChanged); + // Input Combobox current text changed + QObject::connect(ui->inputsCombo, QOverload::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(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(QString("Addr") % QString::number(i+1)); - auto amt = confirm.sendToAddrs->findChild(QString("Amt") % QString::number(i+1)); + auto addr = confirm.sendToAddrs->findChild(QString("Addr") % QString::number(i+1)); + auto amt = confirm.sendToAddrs->findChild(QString("Amt") % QString::number(i+1)); auto memo = confirm.sendToAddrs->findChild(QString("Memo") % QString::number(i+1)); auto amtUSD = confirm.sendToAddrs->findChild(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("labelMinerFee"); @@ -371,25 +371,25 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) { delete confirm.sendToAddrs->findChild("devFee"); delete confirm.sendToAddrs->findChild("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()); - ui->statusBar->showMessage("Computing Tx: " % opid); + rpc->sendZTransaction(params, [=](const json& reply) { + QString opid = QString::fromStdString(reply.get()); + 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); } diff --git a/src/senttxstore.cpp b/src/senttxstore.cpp index 9a17720..a0455e9 100644 --- a/src/senttxstore.cpp +++ b/src/senttxstore.cpp @@ -41,8 +41,8 @@ QList 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 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 diff --git a/src/settings.cpp b/src/settings.cpp index 6f90042..cb60b74 100644 --- a/src/settings.cpp +++ b/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); } \ No newline at end of file diff --git a/src/settings.h b/src/settings.h index 0c08dc2..ecf47d8 100644 --- a/src/settings.h +++ b/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 \ No newline at end of file diff --git a/src/turnstile.cpp b/src/turnstile.cpp index f7dac0a..2c36798 100644 --- a/src/turnstile.cpp +++ b/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 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 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 Turnstile::readMigrationPlan() { - QFile file(writeableFile()); - - QList plan; - if (!file.exists()) return plan; + QFile file(writeableFile()); + + QList 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(splits, - [=] (double /*unused*/) { - json payload = { - {"jsonrpc", "1.0"}, - {"id", "someid"}, - {"method", "getnewaddress"}, - }; - return payload; - }, - [=] (QMap* 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(splits, + [=] (double /*unused*/) { + json payload = { + {"jsonrpc", "1.0"}, + {"id", "someid"}, + {"method", "getnewaddress"}, + }; + return payload; + }, + [=] (QMap* newAddrs) { // Get block numbers - auto curBlock = Settings::getInstance()->getBlockNumber(); - auto blockNumbers = getBlockNumbers(curBlock, curBlock + numBlocks, splits.size()); - - // Assign the amounts to the addresses. - QList migItems; - - for (int i=0; i < splits.size(); i++) { - auto tAddr = newAddrs->values()[i].get(); - 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 migItems; + + for (int i=0; i < splits.size(); i++) { + auto tAddr = newAddrs->values()[i].get(); + 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 Turnstile::getBlockNumbers(int start, int end, int count) { - QList 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 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 Turnstile::splitAmount(double amount, int parts) { - QList 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 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& 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::Iterator Turnstile::getNextStep(QList& 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 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 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 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()); - //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()); + //qDebug() << opid; + mainwindow->ui->statusBar->showMessage("Computing Tx: " % opid); + + // And then start monitoring the transaction + rpc->addNewTxToWatch(tx, opid); + + cb(); + }); } \ No newline at end of file diff --git a/src/turnstile.h b/src/turnstile.h index ae4bf18..a73af50 100644 --- a/src/turnstile.h +++ b/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 splitAmount(double amount, int parts); - void fillAmounts(QList& amounts, double amount, int count); + void planMigration(QString zaddr, QString destAddr, int splits, int numBlocks); + QList splitAmount(double amount, int parts); + void fillAmounts(QList& amounts, double amount, int count); - QList readMigrationPlan(); - void writeMigrationPlan(QList plan); - void removeFile(); - - void executeMigrationStep(); - ProgressReport getPlanProgress(); - bool isMigrationPresent(); + QList readMigrationPlan(); + void writeMigrationPlan(QList plan); + void removeFile(); + + void executeMigrationStep(); + ProgressReport getPlanProgress(); + bool isMigrationPresent(); - static double minMigrationAmount; + static double minMigrationAmount; private: - QList getBlockNumbers(int start, int end, int count); - QString writeableFile(); + QList getBlockNumbers(int start, int end, int count); + QString writeableFile(); - void doSendTx(Tx tx, std::function cb); + void doSendTx(Tx tx, std::function cb); - QList::Iterator getNextStep(QList& plan); + QList::Iterator getNextStep(QList& plan); - RPC* rpc; - MainWindow* mainwindow; + RPC* rpc; + MainWindow* mainwindow; }; #endif diff --git a/src/txtablemodel.cpp b/src/txtablemodel.cpp index 7a3bc00..12664ec 100644 --- a/src/txtablemodel.cpp +++ b/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); diff --git a/src/utils.h b/src/utils.h index feaa9ff..cbe841b 100644 --- a/src/utils.h +++ b/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 \ No newline at end of file