diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index fb050d3..c9bd985 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -381,11 +381,17 @@ void MainWindow::turnstileDoMigration(QString fromAddr) {
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();
+ // If the underlying zcashd has support for the migration, use that.
+ // Else, show the ZecWallet turnstile tool
+ if (rpc->getMigrationStatus()->available) {
+ Turnstile::showZcashdMigration(this);
+ } else {
+ // If there is current migration that is present, show the progress button
+ if (rpc->getTurnstile()->isMigrationPresent())
+ turnstileProgress();
+ else
+ turnstileDoMigration();
+ }
});
}
diff --git a/src/migration.ui b/src/migration.ui
new file mode 100644
index 0000000..3addf20
--- /dev/null
+++ b/src/migration.ui
@@ -0,0 +1,139 @@
+
+
+ MigrationDialog
+
+
+
+ 0
+ 0
+ 511
+ 498
+
+
+
+ Migration Turnstile
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Migration History
+
+
+
-
+
+
+ Migrated Amount
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Unmigrated Amount
+
+
+
+
+
+
+ -
+
+
+ Sprout -> Sapling migration enabled
+
+
+
+ -
+
+
+ If enabled, zcashd will slowly migrate your Sprout shielded funds to your Sapling address.
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ MigrationDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ MigrationDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/rpc.cpp b/src/rpc.cpp
index 761ffc1..2c207cd 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -52,6 +52,9 @@ RPC::RPC(MainWindow* main) {
txTimer->start(Settings::updateSpeed);
usedAddresses = new QMap();
+
+ // Initialize the migration status to unavailable.
+ this->migrationStatus.available = false;
}
RPC::~RPC() {
@@ -568,8 +571,9 @@ void RPC::getInfoThenRefresh(bool force) {
turnstile->executeMigrationStep();
refreshBalances();
- refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans()
+ refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans()
refreshTransactions();
+ refreshMigration(); // Sapling turnstile migration status.
}
int connections = reply["connections"].get();
@@ -755,6 +759,49 @@ bool RPC::processUnspent(const json& reply, QMap* balancesMap,
return anyUnconfirmed;
};
+/**
+ * Refresh the turnstile migration status
+ */
+void RPC::refreshMigration() {
+ // Turnstile migration is only supported in zcashd v2.0.5 and above
+ if (Settings::getInstance()->getZcashdVersion() < 2000552)
+ return;
+
+ json payload = {
+ {"jsonrpc", "1.0"},
+ {"id", "someid"},
+ {"method", "z_getmigrationstatus"},
+ };
+
+ conn->doRPCWithDefaultErrorHandling(payload, [=](json reply) {
+ this->migrationStatus.available = true;
+ this->migrationStatus.enabled = reply["enabled"].get();
+ this->migrationStatus.saplingAddress = QString::fromStdString(reply["destination_address"]);
+ this->migrationStatus.unmigrated = QString::fromStdString(reply["unmigrated_amount"]).toDouble();
+ this->migrationStatus.migrated = QString::fromStdString(reply["finalized_migrated_amount"]).toDouble();
+
+ this->migrationStatus.txids.empty();
+ for (auto& it : reply["migration_txids"].get()) {
+ this->migrationStatus.txids.push_back(QString::fromStdString(it.get()));
+ }
+ });
+}
+
+void RPC::setMigrationStatus(bool enabled) {
+ json payload = {
+ {"jsonrpc", "1.0"},
+ {"id", "someid"},
+ {"method", "z_setmigration"},
+ {"params", {enabled}}
+ };
+
+ conn->doRPCWithDefaultErrorHandling(payload, [=](json) {
+ // Ignore return value.
+ });
+}
+
+
+
void RPC::refreshBalances() {
if (conn == nullptr)
return noConnection();
diff --git a/src/rpc.h b/src/rpc.h
index 7cc6610..bd8bd11 100644
--- a/src/rpc.h
+++ b/src/rpc.h
@@ -31,6 +31,15 @@ struct WatchedTx {
std::function error;
};
+struct MigrationStatus {
+ bool available; // Whether the underlying zcashd supports migration?
+ bool enabled;
+ QString saplingAddress;
+ double unmigrated;
+ double migrated;
+ QList txids;
+};
+
class RPC
{
public:
@@ -88,10 +97,14 @@ public:
Turnstile* getTurnstile() { return turnstile; }
Connection* getConnection() { return conn; }
+ const MigrationStatus* getMigrationStatus() { return &migrationStatus; }
+ void setMigrationStatus(bool enabled);
+
private:
void refreshBalances();
void refreshTransactions();
+ void refreshMigration();
void refreshSentZTrans();
void refreshReceivedZTrans(QList zaddresses);
@@ -108,6 +121,7 @@ private:
void getZAddresses (const std::function& cb);
void getTAddresses (const std::function& cb);
+
Connection* conn = nullptr;
QProcess* ezcashd = nullptr;
@@ -130,6 +144,9 @@ private:
MainWindow* main;
Turnstile* turnstile;
+ // Sapling turnstile migration status (for the zcashd v2.0.5 tool)
+ MigrationStatus migrationStatus;
+
// Current balance in the UI. If this number updates, then refresh the UI
QString currentBalance;
};
diff --git a/src/turnstile.cpp b/src/turnstile.cpp
index f05940b..96d3b3a 100644
--- a/src/turnstile.cpp
+++ b/src/turnstile.cpp
@@ -3,6 +3,8 @@
#include "balancestablemodel.h"
#include "rpc.h"
#include "settings.h"
+#include "ui_migration.h"
+
using json = nlohmann::json;
@@ -375,3 +377,91 @@ void Turnstile::doSendTx(Tx tx, std::function cb) {
});
}
+
+
+// Methods for zcashd native Migration
+void Turnstile::showZcashdMigration(MainWindow* parent) {
+ // If it is not enabled, don't show the dialog
+ if (! parent->getRPC()->getMigrationStatus()->available)
+ return;
+
+ Ui_MigrationDialog md;
+ QDialog d(parent);
+ md.setupUi(&d);
+ Settings::saveRestore(&d);
+
+ MigrationTxns model(md.tblTxids, parent->getRPC()->getMigrationStatus()->txids);
+ md.tblTxids->setModel(&model);
+
+ // Table right click
+ md.tblTxids->setContextMenuPolicy(Qt::CustomContextMenu);
+ QObject::connect(md.tblTxids, &QTableView::customContextMenuRequested, [=, &model] (QPoint pos) {
+ QModelIndex index = md.tblTxids->indexAt(pos);
+ if (index.row() < 0) return;
+
+ QMenu menu(parent);
+ QString txid = model.getTxid(index.row());
+
+ menu.addAction(QObject::tr("View 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));
+ });
+ });
+
+ auto* status = parent->getRPC()->getMigrationStatus();
+
+ md.chkEnabled->setChecked(status->enabled);
+ md.lblSaplingAddress->setText(status->saplingAddress);
+ md.lblUnMigrated->setText(Settings::getZECDisplayFormat(status->unmigrated));
+ md.lblMigrated->setText(Settings::getZECDisplayFormat(status->migrated));
+
+ if (d.exec() == QDialog::Accepted) {
+ // Update the migration status if it changed
+ if (md.chkEnabled->isChecked() != status->enabled) {
+ parent->getRPC()->setMigrationStatus(md.chkEnabled->isChecked());
+ }
+ }
+}
+
+
+MigrationTxns::MigrationTxns(QTableView *parent, QList txids)
+ : QAbstractTableModel(parent) {
+ headers << tr("Txid");
+ this->txids = txids;
+}
+
+
+int MigrationTxns::rowCount(const QModelIndex&) const {
+ return txids.size();
+}
+
+int MigrationTxns::columnCount(const QModelIndex&) const {
+ return headers.size();
+}
+
+QString MigrationTxns::getTxid(int row) const {
+ return txids.at(row);
+}
+
+QVariant MigrationTxns::data(const QModelIndex &index, int role) const {
+ if (role == Qt::DisplayRole) {
+ switch(index.column()) {
+ case 0: return txids.at(index.row());
+ }
+ }
+ return QVariant();
+}
+
+
+QVariant MigrationTxns::headerData(int section, Qt::Orientation orientation, int role) const {
+ if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
+ return headers.at(section);
+ }
+
+ return QVariant();
+}
diff --git a/src/turnstile.h b/src/turnstile.h
index dfa5dae..19643c3 100644
--- a/src/turnstile.h
+++ b/src/turnstile.h
@@ -52,6 +52,8 @@ public:
ProgressReport getPlanProgress();
bool isMigrationPresent();
+ static void showZcashdMigration(MainWindow* parent);
+
static double minMigrationAmount;
private:
QList getBlockNumbers(int start, int end, int count);
@@ -66,4 +68,27 @@ private:
MainWindow* mainwindow;
};
+
+// Classes for zcashd 2.0.5 native migration
+
+
+class MigrationTxns : public QAbstractTableModel {
+
+public:
+ MigrationTxns(QTableView* parent, QList txids);
+ ~MigrationTxns() = default;
+
+ int rowCount(const QModelIndex &parent) const;
+ int columnCount(const QModelIndex &parent) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+ QString getTxid(int row) const;
+
+private:
+ QList txids;
+ QStringList headers;
+};
+
+
#endif
diff --git a/zec-qt-wallet.pro b/zec-qt-wallet.pro
index f88fe01..8ae6977 100644
--- a/zec-qt-wallet.pro
+++ b/zec-qt-wallet.pro
@@ -86,6 +86,7 @@ HEADERS += \
FORMS += \
src/mainwindow.ui \
+ src/migration.ui \
src/recurringpayments.ui \
src/settings.ui \
src/about.ui \