Merge pull request #20 from adityapk00/turnstile
Sapling Turnstile automatic migration
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "turnstile.h"
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@@ -16,6 +18,8 @@ int main(int argc, char *argv[])
|
|||||||
qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false));
|
qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::srand(std::time(nullptr));
|
||||||
|
|
||||||
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
|
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
|
||||||
QCoreApplication::setApplicationName("zec-qt-wallet");
|
QCoreApplication::setApplicationName("zec-qt-wallet");
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "ui_about.h"
|
#include "ui_about.h"
|
||||||
#include "ui_settings.h"
|
#include "ui_settings.h"
|
||||||
|
#include "ui_turnstile.h"
|
||||||
|
#include "ui_turnstileprogress.h"
|
||||||
#include "rpc.h"
|
#include "rpc.h"
|
||||||
#include "balancestablemodel.h"
|
#include "balancestablemodel.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "turnstile.h"
|
||||||
#include "senttxstore.h"
|
#include "senttxstore.h"
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
@@ -56,6 +59,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
setupTransactionsTab();
|
setupTransactionsTab();
|
||||||
setupRecieveTab();
|
setupRecieveTab();
|
||||||
setupBalancesTab();
|
setupBalancesTab();
|
||||||
|
setupTurnstileDialog();
|
||||||
|
|
||||||
rpc = new RPC(new QNetworkAccessManager(this), this);
|
rpc = new RPC(new QNetworkAccessManager(this), this);
|
||||||
rpc->refreshZECPrice();
|
rpc->refreshZECPrice();
|
||||||
@@ -63,6 +67,178 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
rpc->refresh(true); // Force refresh first time
|
rpc->refresh(true); // Force refresh first time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::turnstileProgress() {
|
||||||
|
Ui_TurnstileProgress progress;
|
||||||
|
QDialog d(this);
|
||||||
|
progress.setupUi(&d);
|
||||||
|
|
||||||
|
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
|
||||||
|
progress.msgIcon->setPixmap(icon.pixmap(64, 64));
|
||||||
|
|
||||||
|
auto fnUpdateProgressUI = [=] () {
|
||||||
|
// Get the plan progress
|
||||||
|
if (rpc->getTurnstile()->isMigrationPresent()) {
|
||||||
|
auto curProgress = rpc->getTurnstile()->getPlanProgress();
|
||||||
|
|
||||||
|
progress.progressTxt->setText(QString::number(curProgress.step) % QString(" / ") % QString::number(curProgress.totalSteps));
|
||||||
|
progress.progressBar->setValue(100 * curProgress.step / curProgress.totalSteps);
|
||||||
|
|
||||||
|
auto nextTxBlock = curProgress.nextBlock - Settings::getInstance()->getBlockNumber();
|
||||||
|
|
||||||
|
if (curProgress.step == curProgress.totalSteps) {
|
||||||
|
auto txt = QString("Turnstile migration finished");
|
||||||
|
if (curProgress.hasErrors) {
|
||||||
|
txt = txt + ". There were some errors.\n\nYour funds are all in your wallet, so you should be able to finish moving them manually.";
|
||||||
|
}
|
||||||
|
progress.nextTx->setText(txt);
|
||||||
|
} else {
|
||||||
|
progress.nextTx->setText(QString("Next transaction in ")
|
||||||
|
% QString::number(nextTxBlock < 0 ? 0 : nextTxBlock)
|
||||||
|
% " blocks\n"
|
||||||
|
% (nextTxBlock <= 0 ? "(waiting for confirmations)" : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
progress.progressTxt->setText("");
|
||||||
|
progress.progressBar->setValue(0);
|
||||||
|
progress.nextTx->setText("No turnstile migration is in progress");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTimer progressTimer(this);
|
||||||
|
QObject::connect(&progressTimer, &QTimer::timeout, fnUpdateProgressUI);
|
||||||
|
progressTimer.start(Utils::updateSpeed);
|
||||||
|
fnUpdateProgressUI();
|
||||||
|
|
||||||
|
auto curProgress = rpc->getTurnstile()->getPlanProgress();
|
||||||
|
|
||||||
|
// Abort button
|
||||||
|
if (curProgress.step != curProgress.totalSteps)
|
||||||
|
progress.buttonBox->button(QDialogButtonBox::Discard)->setText("Abort");
|
||||||
|
else
|
||||||
|
progress.buttonBox->button(QDialogButtonBox::Discard)->setVisible(false);
|
||||||
|
QObject::connect(progress.buttonBox->button(QDialogButtonBox::Discard), &QPushButton::clicked, [&] () {
|
||||||
|
if (curProgress.step != curProgress.totalSteps) {
|
||||||
|
auto abort = QMessageBox::warning(this, "Are you sure you want to Abort?",
|
||||||
|
"Are you sure you want to abort the migration?\nAll further transactions will be cancelled.\nAll your funds are still in your wallet.",
|
||||||
|
QMessageBox::Yes, QMessageBox::No);
|
||||||
|
if (abort == QMessageBox::Yes) {
|
||||||
|
rpc->getTurnstile()->removeFile();
|
||||||
|
d.close();
|
||||||
|
ui->statusBar->showMessage("Automatic Sapling turnstile migration aborted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto accpeted = d.exec();
|
||||||
|
if (accpeted == QDialog::Accepted && curProgress.step == curProgress.totalSteps) {
|
||||||
|
// Finished, so delete the file
|
||||||
|
rpc->getTurnstile()->removeFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::turnstileDoMigration() {
|
||||||
|
Ui_Turnstile turnstile;
|
||||||
|
QDialog d(this);
|
||||||
|
turnstile.setupUi(&d);
|
||||||
|
|
||||||
|
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation);
|
||||||
|
turnstile.msgIcon->setPixmap(icon.pixmap(64, 64));
|
||||||
|
|
||||||
|
auto fnGetAllSproutBalance = [=] () {
|
||||||
|
double bal = 0;
|
||||||
|
for (auto addr : *rpc->getAllZAddresses()) {
|
||||||
|
if (Settings::getInstance()->isSproutAddress(addr)) {
|
||||||
|
bal += rpc->getAllBalances()->value(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bal;
|
||||||
|
};
|
||||||
|
|
||||||
|
//turnstile.migrateZaddList->addItem("All Sprout z-Addrs");
|
||||||
|
turnstile.fromBalance->setText(Settings::getInstance()->getZECUSDDisplayFormat(fnGetAllSproutBalance()));
|
||||||
|
for (auto addr : *rpc->getAllZAddresses()) {
|
||||||
|
if (Settings::getInstance()->isSaplingAddress(addr)) {
|
||||||
|
turnstile.migrateTo->addItem(addr);
|
||||||
|
} else {
|
||||||
|
if (rpc->getAllBalances()->value(addr) > 0)
|
||||||
|
turnstile.migrateZaddList->addItem(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fnUpdateSproutBalance = [=] (QString addr) {
|
||||||
|
double bal = 0;
|
||||||
|
if (addr.startsWith("All")) {
|
||||||
|
bal = fnGetAllSproutBalance();
|
||||||
|
} else {
|
||||||
|
bal = rpc->getAllBalances()->value(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto balTxt = Settings::getInstance()->getZECUSDDisplayFormat(bal);
|
||||||
|
|
||||||
|
if (bal < Turnstile::minMigrationAmount) {
|
||||||
|
turnstile.fromBalance->setStyleSheet("color: red;");
|
||||||
|
turnstile.fromBalance->setText(balTxt % " [You need at least "
|
||||||
|
% Settings::getInstance()->getZECDisplayFormat(Turnstile::minMigrationAmount)
|
||||||
|
% " for automatic migration]");
|
||||||
|
turnstile.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
turnstile.fromBalance->setStyleSheet("");
|
||||||
|
turnstile.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||||
|
turnstile.fromBalance->setText(balTxt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fnUpdateSproutBalance(turnstile.migrateZaddList->currentText());
|
||||||
|
|
||||||
|
// Combo box selection event
|
||||||
|
QObject::connect(turnstile.migrateZaddList, &QComboBox::currentTextChanged, fnUpdateSproutBalance);
|
||||||
|
|
||||||
|
// Privacy level combobox
|
||||||
|
// Num tx over num blocks
|
||||||
|
QList<QPair<int, int>> privOptions;
|
||||||
|
privOptions.push_back(QPair<double, double>(3, 6));
|
||||||
|
privOptions.push_back(QPair<double, double>(5, 10));
|
||||||
|
privOptions.push_back(QPair<double, double>(10, 20));
|
||||||
|
|
||||||
|
QObject::connect(turnstile.privLevel, QOverload<int>::of(&QComboBox::currentIndexChanged), [=] (auto idx) {
|
||||||
|
// Update the fees
|
||||||
|
turnstile.minerFee->setText(
|
||||||
|
Settings::getInstance()->getZECUSDDisplayFormat(privOptions[idx].first * Utils::getMinerFee()));
|
||||||
|
});
|
||||||
|
|
||||||
|
turnstile.privLevel->addItem("Good - 3 tx over 6 blocks");
|
||||||
|
turnstile.privLevel->addItem("Excellent - 5 tx over 10 blocks");
|
||||||
|
turnstile.privLevel->addItem("Paranoid - 10 tx over 20 blocks");
|
||||||
|
|
||||||
|
turnstile.buttonBox->button(QDialogButtonBox::Ok)->setText("Start");
|
||||||
|
|
||||||
|
if (d.exec() == QDialog::Accepted) {
|
||||||
|
auto privLevel = privOptions[turnstile.privLevel->currentIndex()];
|
||||||
|
rpc->getTurnstile()->planMigration(
|
||||||
|
turnstile.migrateZaddList->currentText(),
|
||||||
|
turnstile.migrateTo->currentText(),
|
||||||
|
privLevel.first, privLevel.second);
|
||||||
|
|
||||||
|
QMessageBox::information(this, "Backup your wallet.dat",
|
||||||
|
"The migration will now start. You can check progress in the File -> Sapling Turnstile menu.\n\nYOU MUST BACKUP YOUR wallet.dat NOW!\n\nNew Addresses have been added to your wallet which will be used for the migration.",
|
||||||
|
QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupTurnstileDialog() {
|
||||||
|
// Turnstile migration
|
||||||
|
QObject::connect(ui->actionTurnstile_Migration, &QAction::triggered, [=] () {
|
||||||
|
// If there is current migration that is present, show the progress button
|
||||||
|
if (rpc->getTurnstile()->isMigrationPresent())
|
||||||
|
turnstileProgress();
|
||||||
|
else
|
||||||
|
turnstileDoMigration();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::setupStatusBar() {
|
void MainWindow::setupStatusBar() {
|
||||||
// Status Bar
|
// Status Bar
|
||||||
loadingLabel = new QLabel();
|
loadingLabel = new QLabel();
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ private:
|
|||||||
void setupRecieveTab();
|
void setupRecieveTab();
|
||||||
void setupBalancesTab();
|
void setupBalancesTab();
|
||||||
|
|
||||||
|
void setupTurnstileDialog();
|
||||||
void setupSettingsModal();
|
void setupSettingsModal();
|
||||||
void setupStatusBar();
|
void setupStatusBar();
|
||||||
|
|
||||||
@@ -56,7 +57,9 @@ private:
|
|||||||
|
|
||||||
Tx createTxFromSendPage();
|
Tx createTxFromSendPage();
|
||||||
bool confirmTx(Tx tx, ToFields devFee);
|
bool confirmTx(Tx tx, ToFields devFee);
|
||||||
void fillTxJsonParams(json& params, Tx tx);
|
|
||||||
|
void turnstileDoMigration();
|
||||||
|
void turnstileProgress();
|
||||||
|
|
||||||
void cancelButton();
|
void cancelButton();
|
||||||
void sendButton();
|
void sendButton();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>1</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
@@ -362,6 +362,9 @@
|
|||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
<string>Amount</string>
|
<string>Amount</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -723,6 +726,7 @@
|
|||||||
<string>File</string>
|
<string>File</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="actionImport_Private_Keys"/>
|
<addaction name="actionImport_Private_Keys"/>
|
||||||
|
<addaction name="actionTurnstile_Migration"/>
|
||||||
<addaction name="actionSettings"/>
|
<addaction name="actionSettings"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionExit"/>
|
<addaction name="actionExit"/>
|
||||||
@@ -772,6 +776,11 @@
|
|||||||
<string>Check github.com for Updates</string>
|
<string>Check github.com for Updates</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionTurnstile_Migration">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sapling Turnstile</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
/* Add C++ includes here */
|
/* Add C++ includes here */
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
|
|||||||
89
src/rpc.cpp
89
src/rpc.cpp
@@ -2,6 +2,7 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "senttxstore.h"
|
#include "senttxstore.h"
|
||||||
|
#include "turnstile.h"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -10,6 +11,8 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
|||||||
this->main = main;
|
this->main = main;
|
||||||
this->ui = main->ui;
|
this->ui = main->ui;
|
||||||
|
|
||||||
|
this->turnstile = new Turnstile(this, main);
|
||||||
|
|
||||||
// Setup balances table model
|
// Setup balances table model
|
||||||
balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
|
balancesTableModel = new BalancesTableModel(main->ui->balancesTable);
|
||||||
main->ui->balancesTable->setModel(balancesTableModel);
|
main->ui->balancesTable->setModel(balancesTableModel);
|
||||||
@@ -46,6 +49,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
|||||||
});
|
});
|
||||||
// Start at every 10s. When an operation is pending, this will change to every second
|
// Start at every 10s. When an operation is pending, this will change to every second
|
||||||
txTimer->start(Utils::updateSpeed);
|
txTimer->start(Utils::updateSpeed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RPC::~RPC() {
|
RPC::~RPC() {
|
||||||
@@ -54,6 +58,7 @@ RPC::~RPC() {
|
|||||||
|
|
||||||
delete transactionsTableModel;
|
delete transactionsTableModel;
|
||||||
delete balancesTableModel;
|
delete balancesTableModel;
|
||||||
|
delete turnstile;
|
||||||
|
|
||||||
delete utxos;
|
delete utxos;
|
||||||
delete allBalances;
|
delete allBalances;
|
||||||
@@ -285,53 +290,32 @@ void RPC::handleTxError(const QString& error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RPC::getBatchRPC(
|
// Build the RPC JSON Parameters for this tx (with the dev fee included if applicable)
|
||||||
const QList<QString>& payloads,
|
void RPC::fillTxJsonParams(json& params, Tx tx) {
|
||||||
std::function<json(QString)> payloadGenerator,
|
Q_ASSERT(params.is_array());
|
||||||
std::function<void(QMap<QString, json>*)> cb)
|
// Get all the addresses and amounts
|
||||||
{
|
json allRecepients = json::array();
|
||||||
auto responses = new QMap<QString, json>(); // zAddr -> list of responses for each call.
|
|
||||||
int totalSize = payloads.size();
|
|
||||||
|
|
||||||
for (auto item: payloads) {
|
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
|
||||||
json payload = payloadGenerator(item);
|
for (int i=0; i < tx.toAddrs.size(); i++) {
|
||||||
|
auto toAddr = tx.toAddrs[i];
|
||||||
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
// Construct the JSON params
|
||||||
reply->deleteLater();
|
json rec = json::object();
|
||||||
|
rec["address"] = toAddr.addr.toStdString();
|
||||||
auto all = reply->readAll();
|
rec["amount"] = QString::number(toAddr.amount, 'f', 8).toDouble(); // Force it through string for rounding
|
||||||
auto parsed = json::parse(all.toStdString(), nullptr, false);
|
if (toAddr.addr.startsWith("z") && !toAddr.encodedMemo.trimmed().isEmpty())
|
||||||
|
rec["memo"] = toAddr.encodedMemo.toStdString();
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
allRecepients.push_back(rec);
|
||||||
qDebug() << QString::fromStdString(parsed.dump());
|
|
||||||
qDebug() << reply->errorString();
|
|
||||||
|
|
||||||
(*responses)[item] = json::object(); // Empty object
|
|
||||||
} else {
|
|
||||||
if (parsed.is_discarded()) {
|
|
||||||
(*responses)[item] = json::object(); // Empty object
|
|
||||||
} else {
|
|
||||||
(*responses)[item] = parsed["result"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waitTimer = new QTimer(main);
|
// Add sender
|
||||||
QObject::connect(waitTimer, &QTimer::timeout, [=]() {
|
params.push_back(tx.fromAddr.toStdString());
|
||||||
if (responses->size() == totalSize) {
|
params.push_back(allRecepients);
|
||||||
waitTimer->stop();
|
|
||||||
|
|
||||||
cb(responses);
|
|
||||||
|
|
||||||
waitTimer->deleteLater();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
waitTimer->start(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Refresh received z txs by calling z_listreceivedbyaddress/gettransaction
|
// Refresh received z txs by calling z_listreceivedbyaddress/gettransaction
|
||||||
void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
||||||
|
|
||||||
@@ -342,14 +326,13 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This method is complicated because z_listreceivedbyaddress only returns the txid, and
|
// This method is complicated because z_listreceivedbyaddress only returns the txid, and
|
||||||
// we have to make a follow up call to gettransaction to get details of that transaction.
|
// we have to make a follow up call to gettransaction to get details of that transaction.
|
||||||
// Additionally, it has to be done in batches, because there are multiple z-Addresses,
|
// Additionally, it has to be done in batches, because there are multiple z-Addresses,
|
||||||
// and each z-Addr can have multiple received txs.
|
// and each z-Addr can have multiple received txs.
|
||||||
|
|
||||||
// 1. For each z-Addr, get list of received txs
|
// 1. For each z-Addr, get list of received txs
|
||||||
getBatchRPC(zaddrs,
|
getBatchRPC<QString>(zaddrs,
|
||||||
[=] (QString zaddr) {
|
[=] (QString zaddr) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -386,7 +369,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. For all txids, go and get the details of that txid.
|
// 2. For all txids, go and get the details of that txid.
|
||||||
getBatchRPC(txids.toList(),
|
getBatchRPC<QString>(txids.toList(),
|
||||||
[=] (QString txid) {
|
[=] (QString txid) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -527,6 +510,14 @@ void RPC::refreshAddresses() {
|
|||||||
|
|
||||||
// Function to create the data model and update the views, used below.
|
// Function to create the data model and update the views, used below.
|
||||||
void RPC::updateUI(bool anyUnconfirmed) {
|
void RPC::updateUI(bool anyUnconfirmed) {
|
||||||
|
if (Settings::getInstance()->isTestnet()) {
|
||||||
|
// See if the turnstile migration has any steps that need to be done.
|
||||||
|
turnstile->executeMigrationStep();
|
||||||
|
} else {
|
||||||
|
// Not available on mainnet yet.
|
||||||
|
main->ui->actionTurnstile_Migration->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
|
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
|
||||||
|
|
||||||
// Update balances model data, which will update the table too
|
// Update balances model data, which will update the table too
|
||||||
@@ -557,13 +548,9 @@ bool RPC::processUnspent(const json& reply) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
utxos->push_back(
|
utxos->push_back(
|
||||||
UnspentOutput(
|
UnspentOutput{ qsAddr, QString::fromStdString(it["txid"]),
|
||||||
qsAddr,
|
QString::number(it["amount"].get<json::number_float_t>(), 'g', 8),
|
||||||
QString::fromStdString(it["txid"]),
|
(int)confirmations, it["spendable"].get<json::boolean_t>() });
|
||||||
QString::number(it["amount"].get<json::number_float_t>(), 'g', 8),
|
|
||||||
confirmations
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
(*allBalances)[qsAddr] = (*allBalances)[qsAddr] + it["amount"].get<json::number_float_t>();
|
(*allBalances)[qsAddr] = (*allBalances)[qsAddr] + it["amount"].get<json::number_float_t>();
|
||||||
}
|
}
|
||||||
@@ -643,7 +630,7 @@ void RPC::refreshSentZTrans() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up all the txids to get the confirmation count for them.
|
// Look up all the txids to get the confirmation count for them.
|
||||||
getBatchRPC(txids,
|
getBatchRPC<QString>(txids,
|
||||||
[=] (QString txid) {
|
[=] (QString txid) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
|
|||||||
63
src/rpc.h
63
src/rpc.h
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
class Turnstile;
|
||||||
|
|
||||||
struct TransactionItem {
|
struct TransactionItem {
|
||||||
QString type;
|
QString type;
|
||||||
@@ -34,6 +35,7 @@ public:
|
|||||||
void refreshAddresses();
|
void refreshAddresses();
|
||||||
void refreshZECPrice();
|
void refreshZECPrice();
|
||||||
|
|
||||||
|
void fillTxJsonParams(json& params, Tx tx);
|
||||||
void sendZTransaction (json params, const std::function<void(json)>& cb);
|
void sendZTransaction (json params, const std::function<void(json)>& cb);
|
||||||
void watchTxStatus();
|
void watchTxStatus();
|
||||||
void addNewTxToWatch(Tx tx, const QString& newOpid);
|
void addNewTxToWatch(Tx tx, const QString& newOpid);
|
||||||
@@ -45,9 +47,60 @@ public:
|
|||||||
|
|
||||||
void reloadConnectionInfo();
|
void reloadConnectionInfo();
|
||||||
|
|
||||||
void newZaddr (bool sapling, const std::function<void(json)>& cb);
|
void newZaddr(bool sapling, const std::function<void(json)>& cb);
|
||||||
void newTaddr (const std::function<void(json)>& cb);
|
void newTaddr(const std::function<void(json)>& cb);
|
||||||
|
|
||||||
|
Turnstile* getTurnstile() { return turnstile; }
|
||||||
|
|
||||||
|
// Batch method. Note: Because of the template, it has to be in the header file.
|
||||||
|
template<class T>
|
||||||
|
void getBatchRPC(const QList<T>& payloads,
|
||||||
|
std::function<json(T)> payloadGenerator,
|
||||||
|
std::function<void(QMap<T, json>*)> cb) {
|
||||||
|
auto responses = new QMap<T, json>(); // zAddr -> list of responses for each call.
|
||||||
|
int totalSize = payloads.size();
|
||||||
|
|
||||||
|
for (auto item: payloads) {
|
||||||
|
json payload = payloadGenerator(item);
|
||||||
|
|
||||||
|
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
auto all = reply->readAll();
|
||||||
|
auto parsed = json::parse(all.toStdString(), nullptr, false);
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qDebug() << QString::fromStdString(parsed.dump());
|
||||||
|
qDebug() << reply->errorString();
|
||||||
|
|
||||||
|
(*responses)[item] = json::object(); // Empty object
|
||||||
|
} else {
|
||||||
|
if (parsed.is_discarded()) {
|
||||||
|
(*responses)[item] = json::object(); // Empty object
|
||||||
|
} else {
|
||||||
|
(*responses)[item] = parsed["result"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waitTimer = new QTimer(main);
|
||||||
|
QObject::connect(waitTimer, &QTimer::timeout, [=]() {
|
||||||
|
if (responses->size() == totalSize) {
|
||||||
|
waitTimer->stop();
|
||||||
|
|
||||||
|
cb(responses);
|
||||||
|
|
||||||
|
waitTimer->deleteLater();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waitTimer->start(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doRPC (const json& payload, const std::function<void(json)>& cb);
|
void 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);
|
||||||
@@ -73,11 +126,6 @@ private:
|
|||||||
void handleConnectionError (const QString& error);
|
void handleConnectionError (const QString& error);
|
||||||
void handleTxError (const QString& error);
|
void handleTxError (const QString& error);
|
||||||
|
|
||||||
// Batch
|
|
||||||
void getBatchRPC(const QList<QString>& payloads,
|
|
||||||
std::function<json(QString)> payloadGenerator,
|
|
||||||
std::function<void(QMap<QString, json>*)> cb);
|
|
||||||
|
|
||||||
QNetworkAccessManager* restclient;
|
QNetworkAccessManager* restclient;
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
|
|
||||||
@@ -96,6 +144,7 @@ private:
|
|||||||
|
|
||||||
Ui::MainWindow* ui;
|
Ui::MainWindow* ui;
|
||||||
MainWindow* main;
|
MainWindow* main;
|
||||||
|
Turnstile* turnstile;
|
||||||
|
|
||||||
// Current balance in the UI. If this number updates, then refresh the UI
|
// Current balance in the UI. If this number updates, then refresh the UI
|
||||||
QString currentBalance;
|
QString currentBalance;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
void MainWindow::setupSendTab() {
|
void MainWindow::setupSendTab() {
|
||||||
@@ -474,30 +472,6 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the RPC JSON Parameters for this tx (with the dev fee included if applicable)
|
|
||||||
void MainWindow::fillTxJsonParams(json& params, Tx tx) {
|
|
||||||
// Get all the addresses and amounts
|
|
||||||
json allRecepients = json::array();
|
|
||||||
|
|
||||||
// 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];
|
|
||||||
|
|
||||||
// Construct the JSON params
|
|
||||||
json rec = json::object();
|
|
||||||
rec["address"] = toAddr.addr.toStdString();
|
|
||||||
rec["amount"] = toAddr.amount;
|
|
||||||
if (toAddr.addr.startsWith("z") && !toAddr.encodedMemo.trimmed().isEmpty())
|
|
||||||
rec["memo"] = toAddr.encodedMemo.toStdString();
|
|
||||||
|
|
||||||
allRecepients.push_back(rec);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add sender
|
|
||||||
params.push_back(tx.fromAddr.toStdString());
|
|
||||||
params.push_back(allRecepients);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send button clicked
|
// Send button clicked
|
||||||
void MainWindow::sendButton() {
|
void MainWindow::sendButton() {
|
||||||
Tx tx = createTxFromSendPage();
|
Tx tx = createTxFromSendPage();
|
||||||
@@ -522,7 +496,7 @@ void MainWindow::sendButton() {
|
|||||||
tx.toAddrs.push_back(devFee);
|
tx.toAddrs.push_back(devFee);
|
||||||
|
|
||||||
json params = json::array();
|
json params = json::array();
|
||||||
fillTxJsonParams(params, tx);
|
rpc->fillTxJsonParams(params, tx);
|
||||||
std::cout << std::setw(2) << params << std::endl;
|
std::cout << std::setw(2) << params << std::endl;
|
||||||
|
|
||||||
// And send the Tx
|
// And send the Tx
|
||||||
|
|||||||
@@ -168,6 +168,14 @@ bool Settings::isSaplingAddress(QString addr) {
|
|||||||
(!isTestnet() && addr.startsWith("zs"));
|
(!isTestnet() && addr.startsWith("zs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Settings::isSproutAddress(QString addr) {
|
||||||
|
return isZAddress(addr) && !isSaplingAddress(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Settings::isZAddress(QString addr) {
|
||||||
|
return addr.startsWith("z");
|
||||||
|
}
|
||||||
|
|
||||||
bool Settings::isSyncing() {
|
bool Settings::isSyncing() {
|
||||||
return _isSyncing;
|
return _isSyncing;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public:
|
|||||||
void setTestnet(bool isTestnet);
|
void setTestnet(bool isTestnet);
|
||||||
|
|
||||||
bool isSaplingAddress(QString addr);
|
bool isSaplingAddress(QString addr);
|
||||||
|
bool isSproutAddress(QString addr);
|
||||||
|
bool isZAddress(QString addr);
|
||||||
|
|
||||||
bool isSyncing();
|
bool isSyncing();
|
||||||
void setSyncing(bool syncing);
|
void setSyncing(bool syncing);
|
||||||
|
|||||||
369
src/turnstile.cpp
Normal file
369
src/turnstile.cpp
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
#include "turnstile.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "unspentoutput.h"
|
||||||
|
#include "rpc.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
Turnstile::Turnstile(RPC* _rpc, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Turnstile::writeableFile() {
|
||||||
|
auto filename = QStringLiteral("turnstilemigrationplan.dat");
|
||||||
|
|
||||||
|
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||||
|
if (!dir.exists())
|
||||||
|
QDir().mkpath(dir.absolutePath());
|
||||||
|
|
||||||
|
if (Settings::getInstance()->isTestnet()) {
|
||||||
|
return dir.filePath("testnet-" % filename);
|
||||||
|
} else {
|
||||||
|
return dir.filePath(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Turnstile::removeFile() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator>>(QDataStream& ds, TurnstileMigrationItem& item) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TurnstileMigrationItem> Turnstile::readMigrationPlan() {
|
||||||
|
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.close();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (curAmount > 0)
|
||||||
|
amounts.push_back(curAmount);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Turnstile::isMigrationPresent() {
|
||||||
|
auto plan = readMigrationPlan();
|
||||||
|
if (plan.isEmpty()) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressReport Turnstile::getPlanProgress() {
|
||||||
|
auto plan = readMigrationPlan();
|
||||||
|
|
||||||
|
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 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();
|
||||||
|
|
||||||
|
return ProgressReport{(int)step, total*2, nextBlock, hasErrors};
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
67
src/turnstile.h
Normal file
67
src/turnstile.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef TURNSTILE_H
|
||||||
|
#define TURNSTILE_H
|
||||||
|
|
||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
class RPC;
|
||||||
|
class MainWindow;
|
||||||
|
struct Tx;
|
||||||
|
|
||||||
|
|
||||||
|
struct TurnstileMigrationItem {
|
||||||
|
QString fromAddr;
|
||||||
|
QString intTAddr;
|
||||||
|
QString destAddr;
|
||||||
|
int blockNumber;
|
||||||
|
double amount;
|
||||||
|
int status;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TurnstileMigrationItemStatus {
|
||||||
|
NotStarted = 0,
|
||||||
|
SentToT,
|
||||||
|
SentToZS,
|
||||||
|
NotEnoughBalance,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProgressReport {
|
||||||
|
int step;
|
||||||
|
int totalSteps;
|
||||||
|
int nextBlock;
|
||||||
|
bool hasErrors;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Turnstile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
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);
|
||||||
|
|
||||||
|
QList<TurnstileMigrationItem> readMigrationPlan();
|
||||||
|
void writeMigrationPlan(QList<TurnstileMigrationItem> plan);
|
||||||
|
void removeFile();
|
||||||
|
|
||||||
|
void executeMigrationStep();
|
||||||
|
ProgressReport getPlanProgress();
|
||||||
|
bool isMigrationPresent();
|
||||||
|
|
||||||
|
static double minMigrationAmount;
|
||||||
|
private:
|
||||||
|
QList<int> getBlockNumbers(int start, int end, int count);
|
||||||
|
QString writeableFile();
|
||||||
|
|
||||||
|
void doSendTx(Tx tx, std::function<void(void)> cb);
|
||||||
|
|
||||||
|
|
||||||
|
QList<TurnstileMigrationItem>::Iterator getNextStep(QList<TurnstileMigrationItem>& plan);
|
||||||
|
|
||||||
|
RPC* rpc;
|
||||||
|
MainWindow* mainwindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
228
src/turnstile.ui
Normal file
228
src/turnstile.ui
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Turnstile</class>
|
||||||
|
<widget class="QDialog" name="Turnstile">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>565</width>
|
||||||
|
<height>416</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Turnstile Migration</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Turnstile Migration</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="msgIcon">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Privacy Level</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>From</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="migrateZaddList">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="editable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="currentText">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="privLevel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>Funds from Sprout z-Addresses (which start with &quot;zc&quot;) need to be moved to the upgraded Sapling z-Addresses (which start with &quot;zs&quot;). The funds cannot be moved directly, but need to be sent through intermediate &quot;transparent&quot; addresses in privacy-preserving way.</p><p>This migration can be done automatically for you.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>To</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="migrateTo"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="3">
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="fromBalance">
|
||||||
|
<property name="text">
|
||||||
|
<string>Balance</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0" rowspan="2" colspan="3">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Miner Fees</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1" colspan="2">
|
||||||
|
<widget class="QLabel" name="minerFee">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0004 ZEC $0.04</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Total Balance</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Turnstile</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Turnstile</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
157
src/turnstileprogress.ui
Normal file
157
src/turnstileprogress.ui
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>TurnstileProgress</class>
|
||||||
|
<widget class="QDialog" name="TurnstileProgress">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Turnstile Migration Progress</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="3" column="0" colspan="3">
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="value">
|
||||||
|
<number>33</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Please ensure you have your wallet.dat backed up!</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="3">
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="nextTx">
|
||||||
|
<property name="text">
|
||||||
|
<string>Next Transaction in 4 hours</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QLabel" name="progressTxt">
|
||||||
|
<property name="text">
|
||||||
|
<string>4 / 12</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Migration Progress</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="msgIcon">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0" colspan="3">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close|QDialogButtonBox::Discard|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
<property name="centerButtons">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0" colspan="3">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0" colspan="3">
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>TurnstileProgress</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>TurnstileProgress</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
@@ -47,6 +47,7 @@ public:
|
|||||||
QAction *actionDonate;
|
QAction *actionDonate;
|
||||||
QAction *actionImport_Private_Keys;
|
QAction *actionImport_Private_Keys;
|
||||||
QAction *actionCheck_for_Updates;
|
QAction *actionCheck_for_Updates;
|
||||||
|
QAction *actionTurnstile_Migration;
|
||||||
QWidget *centralWidget;
|
QWidget *centralWidget;
|
||||||
QGridLayout *gridLayout_3;
|
QGridLayout *gridLayout_3;
|
||||||
QTabWidget *tabWidget;
|
QTabWidget *tabWidget;
|
||||||
@@ -159,6 +160,8 @@ public:
|
|||||||
actionImport_Private_Keys->setVisible(false);
|
actionImport_Private_Keys->setVisible(false);
|
||||||
actionCheck_for_Updates = new QAction(MainWindow);
|
actionCheck_for_Updates = new QAction(MainWindow);
|
||||||
actionCheck_for_Updates->setObjectName(QStringLiteral("actionCheck_for_Updates"));
|
actionCheck_for_Updates->setObjectName(QStringLiteral("actionCheck_for_Updates"));
|
||||||
|
actionTurnstile_Migration = new QAction(MainWindow);
|
||||||
|
actionTurnstile_Migration->setObjectName(QStringLiteral("actionTurnstile_Migration"));
|
||||||
centralWidget = new QWidget(MainWindow);
|
centralWidget = new QWidget(MainWindow);
|
||||||
centralWidget->setObjectName(QStringLiteral("centralWidget"));
|
centralWidget->setObjectName(QStringLiteral("centralWidget"));
|
||||||
gridLayout_3 = new QGridLayout(centralWidget);
|
gridLayout_3 = new QGridLayout(centralWidget);
|
||||||
@@ -403,6 +406,7 @@ public:
|
|||||||
Amount1 = new QLineEdit(verticalGroupBox);
|
Amount1 = new QLineEdit(verticalGroupBox);
|
||||||
Amount1->setObjectName(QStringLiteral("Amount1"));
|
Amount1->setObjectName(QStringLiteral("Amount1"));
|
||||||
Amount1->setBaseSize(QSize(200, 0));
|
Amount1->setBaseSize(QSize(200, 0));
|
||||||
|
Amount1->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
|
||||||
|
|
||||||
horizontalLayout_13->addWidget(Amount1);
|
horizontalLayout_13->addWidget(Amount1);
|
||||||
|
|
||||||
@@ -667,6 +671,7 @@ public:
|
|||||||
menuBar->addAction(menuBalance->menuAction());
|
menuBar->addAction(menuBalance->menuAction());
|
||||||
menuBar->addAction(menuHelp->menuAction());
|
menuBar->addAction(menuHelp->menuAction());
|
||||||
menuBalance->addAction(actionImport_Private_Keys);
|
menuBalance->addAction(actionImport_Private_Keys);
|
||||||
|
menuBalance->addAction(actionTurnstile_Migration);
|
||||||
menuBalance->addAction(actionSettings);
|
menuBalance->addAction(actionSettings);
|
||||||
menuBalance->addSeparator();
|
menuBalance->addSeparator();
|
||||||
menuBalance->addAction(actionExit);
|
menuBalance->addAction(actionExit);
|
||||||
@@ -676,7 +681,7 @@ public:
|
|||||||
|
|
||||||
retranslateUi(MainWindow);
|
retranslateUi(MainWindow);
|
||||||
|
|
||||||
tabWidget->setCurrentIndex(1);
|
tabWidget->setCurrentIndex(0);
|
||||||
|
|
||||||
|
|
||||||
QMetaObject::connectSlotsByName(MainWindow);
|
QMetaObject::connectSlotsByName(MainWindow);
|
||||||
@@ -691,6 +696,7 @@ public:
|
|||||||
actionDonate->setText(QApplication::translate("MainWindow", "Donate", nullptr));
|
actionDonate->setText(QApplication::translate("MainWindow", "Donate", nullptr));
|
||||||
actionImport_Private_Keys->setText(QApplication::translate("MainWindow", "Import Private Keys", nullptr));
|
actionImport_Private_Keys->setText(QApplication::translate("MainWindow", "Import Private Keys", nullptr));
|
||||||
actionCheck_for_Updates->setText(QApplication::translate("MainWindow", "Check github.com for Updates", nullptr));
|
actionCheck_for_Updates->setText(QApplication::translate("MainWindow", "Check github.com for Updates", nullptr));
|
||||||
|
actionTurnstile_Migration->setText(QApplication::translate("MainWindow", "Sapling Turnstile", nullptr));
|
||||||
groupBox->setTitle(QApplication::translate("MainWindow", "Summary", nullptr));
|
groupBox->setTitle(QApplication::translate("MainWindow", "Summary", nullptr));
|
||||||
label->setText(QApplication::translate("MainWindow", "Shielded", nullptr));
|
label->setText(QApplication::translate("MainWindow", "Shielded", nullptr));
|
||||||
balSheilded->setText(QString());
|
balSheilded->setText(QString());
|
||||||
|
|||||||
205
src/ui_turnstile.h
Normal file
205
src/ui_turnstile.h
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
** Form generated from reading UI file 'turnstile.ui'
|
||||||
|
**
|
||||||
|
** Created by: Qt User Interface Compiler version 5.11.2
|
||||||
|
**
|
||||||
|
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#ifndef UI_TURNSTILE_H
|
||||||
|
#define UI_TURNSTILE_H
|
||||||
|
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtWidgets/QComboBox>
|
||||||
|
#include <QtWidgets/QDialog>
|
||||||
|
#include <QtWidgets/QDialogButtonBox>
|
||||||
|
#include <QtWidgets/QFrame>
|
||||||
|
#include <QtWidgets/QGridLayout>
|
||||||
|
#include <QtWidgets/QGroupBox>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QSpacerItem>
|
||||||
|
#include <QtWidgets/QVBoxLayout>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class Ui_Turnstile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QVBoxLayout *verticalLayout;
|
||||||
|
QGroupBox *groupBox;
|
||||||
|
QVBoxLayout *verticalLayout_2;
|
||||||
|
QGridLayout *gridLayout;
|
||||||
|
QLabel *msgIcon;
|
||||||
|
QLabel *label_2;
|
||||||
|
QLabel *label;
|
||||||
|
QComboBox *migrateZaddList;
|
||||||
|
QComboBox *privLevel;
|
||||||
|
QLabel *label_8;
|
||||||
|
QLabel *label_9;
|
||||||
|
QComboBox *migrateTo;
|
||||||
|
QFrame *line;
|
||||||
|
QLabel *fromBalance;
|
||||||
|
QSpacerItem *verticalSpacer;
|
||||||
|
QLabel *label_5;
|
||||||
|
QLabel *minerFee;
|
||||||
|
QLabel *label_3;
|
||||||
|
QDialogButtonBox *buttonBox;
|
||||||
|
|
||||||
|
void setupUi(QDialog *Turnstile)
|
||||||
|
{
|
||||||
|
if (Turnstile->objectName().isEmpty())
|
||||||
|
Turnstile->setObjectName(QStringLiteral("Turnstile"));
|
||||||
|
Turnstile->resize(565, 416);
|
||||||
|
verticalLayout = new QVBoxLayout(Turnstile);
|
||||||
|
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||||
|
groupBox = new QGroupBox(Turnstile);
|
||||||
|
groupBox->setObjectName(QStringLiteral("groupBox"));
|
||||||
|
verticalLayout_2 = new QVBoxLayout(groupBox);
|
||||||
|
verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2"));
|
||||||
|
gridLayout = new QGridLayout();
|
||||||
|
gridLayout->setObjectName(QStringLiteral("gridLayout"));
|
||||||
|
msgIcon = new QLabel(groupBox);
|
||||||
|
msgIcon->setObjectName(QStringLiteral("msgIcon"));
|
||||||
|
msgIcon->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
gridLayout->addWidget(msgIcon, 0, 0, 1, 1);
|
||||||
|
|
||||||
|
label_2 = new QLabel(groupBox);
|
||||||
|
label_2->setObjectName(QStringLiteral("label_2"));
|
||||||
|
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||||
|
sizePolicy.setHorizontalStretch(0);
|
||||||
|
sizePolicy.setVerticalStretch(0);
|
||||||
|
sizePolicy.setHeightForWidth(label_2->sizePolicy().hasHeightForWidth());
|
||||||
|
label_2->setSizePolicy(sizePolicy);
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_2, 5, 0, 1, 1);
|
||||||
|
|
||||||
|
label = new QLabel(groupBox);
|
||||||
|
label->setObjectName(QStringLiteral("label"));
|
||||||
|
sizePolicy.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
|
||||||
|
label->setSizePolicy(sizePolicy);
|
||||||
|
|
||||||
|
gridLayout->addWidget(label, 2, 0, 1, 1);
|
||||||
|
|
||||||
|
migrateZaddList = new QComboBox(groupBox);
|
||||||
|
migrateZaddList->setObjectName(QStringLiteral("migrateZaddList"));
|
||||||
|
QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
sizePolicy1.setHorizontalStretch(0);
|
||||||
|
sizePolicy1.setVerticalStretch(0);
|
||||||
|
sizePolicy1.setHeightForWidth(migrateZaddList->sizePolicy().hasHeightForWidth());
|
||||||
|
migrateZaddList->setSizePolicy(sizePolicy1);
|
||||||
|
migrateZaddList->setEditable(false);
|
||||||
|
|
||||||
|
gridLayout->addWidget(migrateZaddList, 2, 1, 1, 2);
|
||||||
|
|
||||||
|
privLevel = new QComboBox(groupBox);
|
||||||
|
privLevel->setObjectName(QStringLiteral("privLevel"));
|
||||||
|
sizePolicy1.setHeightForWidth(privLevel->sizePolicy().hasHeightForWidth());
|
||||||
|
privLevel->setSizePolicy(sizePolicy1);
|
||||||
|
|
||||||
|
gridLayout->addWidget(privLevel, 5, 1, 1, 2);
|
||||||
|
|
||||||
|
label_8 = new QLabel(groupBox);
|
||||||
|
label_8->setObjectName(QStringLiteral("label_8"));
|
||||||
|
label_8->setWordWrap(true);
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_8, 0, 1, 1, 2);
|
||||||
|
|
||||||
|
label_9 = new QLabel(groupBox);
|
||||||
|
label_9->setObjectName(QStringLiteral("label_9"));
|
||||||
|
sizePolicy.setHeightForWidth(label_9->sizePolicy().hasHeightForWidth());
|
||||||
|
label_9->setSizePolicy(sizePolicy);
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_9, 4, 0, 1, 1);
|
||||||
|
|
||||||
|
migrateTo = new QComboBox(groupBox);
|
||||||
|
migrateTo->setObjectName(QStringLiteral("migrateTo"));
|
||||||
|
|
||||||
|
gridLayout->addWidget(migrateTo, 4, 1, 1, 2);
|
||||||
|
|
||||||
|
line = new QFrame(groupBox);
|
||||||
|
line->setObjectName(QStringLiteral("line"));
|
||||||
|
line->setFrameShape(QFrame::HLine);
|
||||||
|
line->setFrameShadow(QFrame::Sunken);
|
||||||
|
|
||||||
|
gridLayout->addWidget(line, 1, 0, 1, 3);
|
||||||
|
|
||||||
|
fromBalance = new QLabel(groupBox);
|
||||||
|
fromBalance->setObjectName(QStringLiteral("fromBalance"));
|
||||||
|
|
||||||
|
gridLayout->addWidget(fromBalance, 3, 1, 1, 1);
|
||||||
|
|
||||||
|
verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
gridLayout->addItem(verticalSpacer, 7, 0, 2, 3);
|
||||||
|
|
||||||
|
label_5 = new QLabel(groupBox);
|
||||||
|
label_5->setObjectName(QStringLiteral("label_5"));
|
||||||
|
sizePolicy.setHeightForWidth(label_5->sizePolicy().hasHeightForWidth());
|
||||||
|
label_5->setSizePolicy(sizePolicy);
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_5, 6, 0, 1, 1);
|
||||||
|
|
||||||
|
minerFee = new QLabel(groupBox);
|
||||||
|
minerFee->setObjectName(QStringLiteral("minerFee"));
|
||||||
|
QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
|
sizePolicy2.setHorizontalStretch(0);
|
||||||
|
sizePolicy2.setVerticalStretch(0);
|
||||||
|
sizePolicy2.setHeightForWidth(minerFee->sizePolicy().hasHeightForWidth());
|
||||||
|
minerFee->setSizePolicy(sizePolicy2);
|
||||||
|
minerFee->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
|
||||||
|
|
||||||
|
gridLayout->addWidget(minerFee, 6, 1, 1, 2);
|
||||||
|
|
||||||
|
label_3 = new QLabel(groupBox);
|
||||||
|
label_3->setObjectName(QStringLiteral("label_3"));
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_3, 3, 0, 1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
verticalLayout_2->addLayout(gridLayout);
|
||||||
|
|
||||||
|
|
||||||
|
verticalLayout->addWidget(groupBox);
|
||||||
|
|
||||||
|
buttonBox = new QDialogButtonBox(Turnstile);
|
||||||
|
buttonBox->setObjectName(QStringLiteral("buttonBox"));
|
||||||
|
buttonBox->setOrientation(Qt::Horizontal);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||||
|
|
||||||
|
verticalLayout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
|
||||||
|
retranslateUi(Turnstile);
|
||||||
|
QObject::connect(buttonBox, SIGNAL(accepted()), Turnstile, SLOT(accept()));
|
||||||
|
QObject::connect(buttonBox, SIGNAL(rejected()), Turnstile, SLOT(reject()));
|
||||||
|
|
||||||
|
QMetaObject::connectSlotsByName(Turnstile);
|
||||||
|
} // setupUi
|
||||||
|
|
||||||
|
void retranslateUi(QDialog *Turnstile)
|
||||||
|
{
|
||||||
|
Turnstile->setWindowTitle(QApplication::translate("Turnstile", "Turnstile Migration", nullptr));
|
||||||
|
groupBox->setTitle(QApplication::translate("Turnstile", "Turnstile Migration", nullptr));
|
||||||
|
msgIcon->setText(QString());
|
||||||
|
label_2->setText(QApplication::translate("Turnstile", "Privacy Level", nullptr));
|
||||||
|
label->setText(QApplication::translate("Turnstile", "From", nullptr));
|
||||||
|
migrateZaddList->setCurrentText(QString());
|
||||||
|
label_8->setText(QApplication::translate("Turnstile", "<html><head/><body><p>Funds from Sprout z-Addresses (which start with "zc") need to be moved to the upgraded Sapling z-Addresses (which start with "zs"). The funds cannot be moved directly, but need to be sent through intermediate "transparent" addresses in privacy-preserving way.</p><p>This migration can be done automatically for you.</p></body></html>", nullptr));
|
||||||
|
label_9->setText(QApplication::translate("Turnstile", "To", nullptr));
|
||||||
|
fromBalance->setText(QApplication::translate("Turnstile", "Balance", nullptr));
|
||||||
|
label_5->setText(QApplication::translate("Turnstile", "Miner Fees", nullptr));
|
||||||
|
minerFee->setText(QApplication::translate("Turnstile", "0.0004 ZEC $0.04", nullptr));
|
||||||
|
label_3->setText(QApplication::translate("Turnstile", "Total Balance", nullptr));
|
||||||
|
} // retranslateUi
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Turnstile: public Ui_Turnstile {};
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // UI_TURNSTILE_H
|
||||||
142
src/ui_turnstileprogress.h
Normal file
142
src/ui_turnstileprogress.h
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
** Form generated from reading UI file 'turnstileprogress.ui'
|
||||||
|
**
|
||||||
|
** Created by: Qt User Interface Compiler version 5.11.2
|
||||||
|
**
|
||||||
|
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#ifndef UI_TURNSTILEPROGRESS_H
|
||||||
|
#define UI_TURNSTILEPROGRESS_H
|
||||||
|
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtWidgets/QDialog>
|
||||||
|
#include <QtWidgets/QDialogButtonBox>
|
||||||
|
#include <QtWidgets/QFrame>
|
||||||
|
#include <QtWidgets/QGridLayout>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QProgressBar>
|
||||||
|
#include <QtWidgets/QSpacerItem>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class Ui_TurnstileProgress
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QGridLayout *gridLayout;
|
||||||
|
QProgressBar *progressBar;
|
||||||
|
QLabel *label_4;
|
||||||
|
QFrame *line;
|
||||||
|
QLabel *nextTx;
|
||||||
|
QLabel *progressTxt;
|
||||||
|
QLabel *label_2;
|
||||||
|
QLabel *msgIcon;
|
||||||
|
QDialogButtonBox *buttonBox;
|
||||||
|
QSpacerItem *verticalSpacer;
|
||||||
|
QFrame *line_2;
|
||||||
|
|
||||||
|
void setupUi(QDialog *TurnstileProgress)
|
||||||
|
{
|
||||||
|
if (TurnstileProgress->objectName().isEmpty())
|
||||||
|
TurnstileProgress->setObjectName(QStringLiteral("TurnstileProgress"));
|
||||||
|
TurnstileProgress->resize(400, 300);
|
||||||
|
gridLayout = new QGridLayout(TurnstileProgress);
|
||||||
|
gridLayout->setObjectName(QStringLiteral("gridLayout"));
|
||||||
|
progressBar = new QProgressBar(TurnstileProgress);
|
||||||
|
progressBar->setObjectName(QStringLiteral("progressBar"));
|
||||||
|
progressBar->setValue(33);
|
||||||
|
|
||||||
|
gridLayout->addWidget(progressBar, 3, 0, 1, 3);
|
||||||
|
|
||||||
|
label_4 = new QLabel(TurnstileProgress);
|
||||||
|
label_4->setObjectName(QStringLiteral("label_4"));
|
||||||
|
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
|
sizePolicy.setHorizontalStretch(0);
|
||||||
|
sizePolicy.setVerticalStretch(0);
|
||||||
|
sizePolicy.setHeightForWidth(label_4->sizePolicy().hasHeightForWidth());
|
||||||
|
label_4->setSizePolicy(sizePolicy);
|
||||||
|
label_4->setWordWrap(true);
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_4, 7, 1, 1, 2);
|
||||||
|
|
||||||
|
line = new QFrame(TurnstileProgress);
|
||||||
|
line->setObjectName(QStringLiteral("line"));
|
||||||
|
line->setFrameShape(QFrame::HLine);
|
||||||
|
line->setFrameShadow(QFrame::Sunken);
|
||||||
|
|
||||||
|
gridLayout->addWidget(line, 4, 0, 1, 3);
|
||||||
|
|
||||||
|
nextTx = new QLabel(TurnstileProgress);
|
||||||
|
nextTx->setObjectName(QStringLiteral("nextTx"));
|
||||||
|
|
||||||
|
gridLayout->addWidget(nextTx, 5, 0, 1, 3);
|
||||||
|
|
||||||
|
progressTxt = new QLabel(TurnstileProgress);
|
||||||
|
progressTxt->setObjectName(QStringLiteral("progressTxt"));
|
||||||
|
progressTxt->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
|
||||||
|
|
||||||
|
gridLayout->addWidget(progressTxt, 2, 2, 1, 1);
|
||||||
|
|
||||||
|
label_2 = new QLabel(TurnstileProgress);
|
||||||
|
label_2->setObjectName(QStringLiteral("label_2"));
|
||||||
|
|
||||||
|
gridLayout->addWidget(label_2, 2, 0, 1, 2);
|
||||||
|
|
||||||
|
msgIcon = new QLabel(TurnstileProgress);
|
||||||
|
msgIcon->setObjectName(QStringLiteral("msgIcon"));
|
||||||
|
QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||||
|
sizePolicy1.setHorizontalStretch(0);
|
||||||
|
sizePolicy1.setVerticalStretch(0);
|
||||||
|
sizePolicy1.setHeightForWidth(msgIcon->sizePolicy().hasHeightForWidth());
|
||||||
|
msgIcon->setSizePolicy(sizePolicy1);
|
||||||
|
msgIcon->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
gridLayout->addWidget(msgIcon, 7, 0, 1, 1);
|
||||||
|
|
||||||
|
buttonBox = new QDialogButtonBox(TurnstileProgress);
|
||||||
|
buttonBox->setObjectName(QStringLiteral("buttonBox"));
|
||||||
|
buttonBox->setOrientation(Qt::Horizontal);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Close|QDialogButtonBox::Discard|QDialogButtonBox::Ok);
|
||||||
|
buttonBox->setCenterButtons(false);
|
||||||
|
|
||||||
|
gridLayout->addWidget(buttonBox, 9, 0, 1, 3);
|
||||||
|
|
||||||
|
verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
gridLayout->addItem(verticalSpacer, 6, 0, 1, 3);
|
||||||
|
|
||||||
|
line_2 = new QFrame(TurnstileProgress);
|
||||||
|
line_2->setObjectName(QStringLiteral("line_2"));
|
||||||
|
line_2->setFrameShape(QFrame::HLine);
|
||||||
|
line_2->setFrameShadow(QFrame::Sunken);
|
||||||
|
|
||||||
|
gridLayout->addWidget(line_2, 8, 0, 1, 3);
|
||||||
|
|
||||||
|
|
||||||
|
retranslateUi(TurnstileProgress);
|
||||||
|
QObject::connect(buttonBox, SIGNAL(accepted()), TurnstileProgress, SLOT(accept()));
|
||||||
|
QObject::connect(buttonBox, SIGNAL(rejected()), TurnstileProgress, SLOT(reject()));
|
||||||
|
|
||||||
|
QMetaObject::connectSlotsByName(TurnstileProgress);
|
||||||
|
} // setupUi
|
||||||
|
|
||||||
|
void retranslateUi(QDialog *TurnstileProgress)
|
||||||
|
{
|
||||||
|
TurnstileProgress->setWindowTitle(QApplication::translate("TurnstileProgress", "Turnstile Migration Progress", nullptr));
|
||||||
|
label_4->setText(QApplication::translate("TurnstileProgress", "Please ensure you have your wallet.dat backed up!", nullptr));
|
||||||
|
nextTx->setText(QApplication::translate("TurnstileProgress", "Next Transaction in 4 hours", nullptr));
|
||||||
|
progressTxt->setText(QApplication::translate("TurnstileProgress", "4 / 12", nullptr));
|
||||||
|
label_2->setText(QApplication::translate("TurnstileProgress", "Migration Progress", nullptr));
|
||||||
|
msgIcon->setText(QApplication::translate("TurnstileProgress", "TextLabel", nullptr));
|
||||||
|
} // retranslateUi
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class TurnstileProgress: public Ui_TurnstileProgress {};
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // UI_TURNSTILEPROGRESS_H
|
||||||
@@ -1,9 +1 @@
|
|||||||
#include "unspentoutput.h"
|
#include "unspentoutput.h"
|
||||||
|
|
||||||
UnspentOutput::UnspentOutput(QString address, QString txid, QString amount, int confirmations)
|
|
||||||
{
|
|
||||||
this->address = address;
|
|
||||||
this->txid = txid;
|
|
||||||
this->amount = amount;
|
|
||||||
this->confirmations = confirmations;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,15 +3,12 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
class UnspentOutput
|
struct UnspentOutput {
|
||||||
{
|
|
||||||
public:
|
|
||||||
UnspentOutput(QString address, QString txid, QString amount, int confirmations);
|
|
||||||
|
|
||||||
QString address;
|
QString address;
|
||||||
QString txid;
|
QString txid;
|
||||||
QString amount;
|
QString amount;
|
||||||
int confirmations;
|
int confirmations;
|
||||||
|
bool spendable;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,17 @@ const QString Utils::getDonationAddr(bool sapling) {
|
|||||||
return "zcEgrceTwvoiFdEvPWcsJHAMrpLsprMF6aRJiQa3fan5ZphyXLPuHghnEPrEPRoEVzUy65GnMVyCTRdkT6BYBepnXh6NBYs";
|
return "zcEgrceTwvoiFdEvPWcsJHAMrpLsprMF6aRJiQa3fan5ZphyXLPuHghnEPrEPRoEVzUy65GnMVyCTRdkT6BYBepnXh6NBYs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString Utils::getDevSproutAddr() {
|
||||||
|
return "ztbGDqgkmXQjheivgeirwEvJLD4SUNqsWCGwxwVg4YpGz1ARR8P2rXaptkT14z3NDKamcxNmQuvmvktyokMs7HkutRNSx1D";
|
||||||
|
}
|
||||||
|
|
||||||
// Get the dev fee address based on the transaction
|
// Get the dev fee address based on the transaction
|
||||||
const QString Utils::getDevAddr(Tx tx) {
|
const QString Utils::getDevAddr(Tx tx) {
|
||||||
auto testnetAddrLookup = [=] (const QString& addr) -> QString {
|
auto testnetAddrLookup = [=] (const QString& addr) -> QString {
|
||||||
if (addr.startsWith("ztestsapling")) {
|
if (addr.startsWith("ztestsapling")) {
|
||||||
return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh";
|
return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh";
|
||||||
} else if (addr.startsWith("zt")) {
|
} else if (addr.startsWith("zt")) {
|
||||||
return "ztbGDqgkmXQjheivgeirwEvJLD4SUNqsWCGwxwVg4YpGz1ARR8P2rXaptkT14z3NDKamcxNmQuvmvktyokMs7HkutRNSx1D";
|
return getDevSproutAddr();
|
||||||
} else {
|
} else {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ public:
|
|||||||
static const QString txidStatusMessage;
|
static const QString txidStatusMessage;
|
||||||
|
|
||||||
static const QString getTokenName();
|
static const QString getTokenName();
|
||||||
|
static const QString getDevSproutAddr();
|
||||||
static const QString getDevAddr(Tx tx);
|
static const QString getDevAddr(Tx tx);
|
||||||
static const QString getDonationAddr(bool sapling);
|
static const QString getDonationAddr(bool sapling);
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ SOURCES += \
|
|||||||
src/sendtab.cpp \
|
src/sendtab.cpp \
|
||||||
src/senttxstore.cpp \
|
src/senttxstore.cpp \
|
||||||
src/txtablemodel.cpp \
|
src/txtablemodel.cpp \
|
||||||
|
src/turnstile.cpp \
|
||||||
src/utils.cpp
|
src/utils.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
@@ -65,6 +66,7 @@ HEADERS += \
|
|||||||
src/settings.h \
|
src/settings.h \
|
||||||
src/txtablemodel.h \
|
src/txtablemodel.h \
|
||||||
src/senttxstore.h \
|
src/senttxstore.h \
|
||||||
|
src/turnstile.h \
|
||||||
src/utils.h
|
src/utils.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
@@ -72,6 +74,8 @@ FORMS += \
|
|||||||
src/settings.ui \
|
src/settings.ui \
|
||||||
src/about.ui \
|
src/about.ui \
|
||||||
src/confirm.ui \
|
src/confirm.ui \
|
||||||
|
src/turnstile.ui \
|
||||||
|
src/turnstileprogress.ui
|
||||||
src/memodialog.ui
|
src/memodialog.ui
|
||||||
|
|
||||||
# Default rules for deployment.
|
# Default rules for deployment.
|
||||||
|
|||||||
Reference in New Issue
Block a user