Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullifier migration tool #18

Merged
merged 3 commits into from Aug 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
113 changes: 99 additions & 14 deletions src/mainwindow.cpp
Expand Up @@ -5,6 +5,7 @@
#include "ui_mainwindow.h"
#include "ui_mobileappconnector.h"
#include "ui_addressbook.h"
#include "ui_nullifiermigration.h"
#include "ui_zboard.h"
#include "ui_privkey.h"
#include "ui_about.h"
Expand Down Expand Up @@ -75,6 +76,9 @@ MainWindow::MainWindow(QWidget *parent) :
// Validate Address
QObject::connect(ui->actionValidate_Address, &QAction::triggered, this, &MainWindow::validateAddress);

// Nullifier Migration
QObject::connect(ui->actionNullifier_Migration, &QAction::triggered, this, &MainWindow::nullifierMigration);

// Connect mobile app
QObject::connect(ui->actionConnect_Mobile_App, &QAction::triggered, this, [=] () {
if (rpc->getConnection() == nullptr)
Expand Down Expand Up @@ -631,6 +635,100 @@ void MainWindow::donate() {
ui->tabWidget->setCurrentIndex(1);
}

/** Migrate sapling nullifiers */
void MainWindow::nullifierMigration() {
// Make sure everything is up and running
if (!getRPC() || !getRPC()->getConnection())
return;

QDialog* d = new QDialog(this);
Ui_NullifierMigrationDialog* nm = new Ui_NullifierMigrationDialog();
nm->setupUi(d);
Settings::saveRestore(d);


auto saplingBalances = new QList<QPair<QString, double>>();
auto possibleDestinations = new QStringList();

// Populate the table with sapling balances
auto balances = getRPC()->getAllBalances();
auto zaddrs = getRPC()->getAllZAddresses();
for (auto z: *zaddrs) {
if (Settings::getInstance()->isSaplingAddress(z)) {
if (balances->value(z) == 0) {
*possibleDestinations << z;
} else {
saplingBalances->push_back(QPair<QString, double>(z, balances->value(z)));
}
}
}

nm->balancesTable->setRowCount(saplingBalances->size());
nm->balancesTable->setColumnCount(2);

QStringList headers;
headers << tr("Address") << tr("Balance");
nm->balancesTable->setHorizontalHeaderLabels(headers);
nm->balancesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
nm->balancesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);

for (int row = 0; row < saplingBalances->size(); row++) {
nm->balancesTable->setItem(row, 0, new QTableWidgetItem(saplingBalances->value(row).first));
nm->balancesTable->setItem(row, 1, new QTableWidgetItem(Settings::getZECDisplayFormat(saplingBalances->value(row).second)));
}

auto fnShowDialog = [=] () {
for (auto a : *possibleDestinations) {
nm->cmbAddresses->addItem(a, 0);
}

if (d->exec() == QDialog::Accepted) {
auto destAddr = nm->cmbAddresses->currentText();

// Create and send all the transactions
QList<Tx> transactions;
for (auto fromAddr: *saplingBalances) {
Tx tx;
tx.fromAddr = fromAddr.first;
auto memo = "Nullifier Migration Transaction from " + fromAddr.first;
tx.toAddrs.push_back( ToFields{destAddr, fromAddr.second - Settings::getMinerFee(), memo, memo.toUtf8().toHex()} );
tx.fee = Settings::getMinerFee();

transactions.push_back(tx);
}

// Then execute all the transactions
for (auto tx: transactions) {
getRPC()->executeStandardUITransaction(tx);
}

// Tell the user to backup the wallet.
QMessageBox::information(this, tr("Migration Started"),
tr("The nullifier migration transactions will not be executed.\nPlease make sure you BACKUP YOUR WALLET NOW!"),
QMessageBox::Ok);
};

delete saplingBalances;
delete possibleDestinations;
delete nm;
delete d;
};

// We need a possible destination that is not in the list of balances, since we can't send a z_sendmany
// transaction to the same address.
// If there isn't a possible destination, create one.
if (possibleDestinations->isEmpty()) {
getRPC()->newZaddr(true, [=] (const json& reply) {
QString addr = QString::fromStdString(reply.get<json::string_t>());
*possibleDestinations << addr;
fnShowDialog();
});
} else {
fnShowDialog();
}

}

/**
* Validate an address
*/
Expand Down Expand Up @@ -773,20 +871,7 @@ void MainWindow::postToZBoard() {
tx.fee = Settings::getMinerFee();

// And send the Tx
rpc->executeTransaction(tx, [=] (QString opid) {
ui->statusBar->showMessage(tr("Computing Tx: ") % opid);
},
[=] (QString /*opid*/, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000);

if (!opid.isEmpty())
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr;

QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
});
rpc->executeStandardUITransaction(tx);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/mainwindow.h
Expand Up @@ -57,6 +57,7 @@ class MainWindow : public QMainWindow
void payZcashURI(QString uri = "", QString myAddr = "");

void validateAddress();
void nullifierMigration();

void updateLabels();
void updateTAddrCombo(bool checked);
Expand Down
12 changes: 9 additions & 3 deletions src/mainwindow.ui
Expand Up @@ -359,8 +359,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>928</width>
<height>353</height>
<width>920</width>
<height>301</height>
</rect>
</property>
<layout class="QVBoxLayout" name="sendToLayout">
Expand Down Expand Up @@ -1052,7 +1052,7 @@
<x>0</x>
<y>0</y>
<width>968</width>
<height>19</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
Expand Down Expand Up @@ -1085,6 +1085,7 @@
<addaction name="actionConnect_Mobile_App"/>
<addaction name="actionValidate_Address"/>
<addaction name="separator"/>
<addaction name="actionNullifier_Migration"/>
<addaction name="actionTurnstile_Migration"/>
</widget>
<widget class="QMenu" name="menu_Edit">
Expand Down Expand Up @@ -1195,6 +1196,11 @@
<string>Validate Address</string>
</property>
</action>
<action name="actionNullifier_Migration">
<property name="text">
<string>Nullifier Migration</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
Expand Down
119 changes: 119 additions & 0 deletions src/nullifiermigration.ui
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NullifierMigrationDialog</class>
<widget class="QDialog" name="NullifierMigrationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>585</width>
<height>656</height>
</rect>
</property>
<property name="windowTitle">
<string>Nullifier Migration</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="6" column="0">
<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>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>This tool will send all your Sapling YEC to yourself (to a new address), in order to reset the nullifiers used.
All YEC from the following addresses will be consolidated into the destination address</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="AddressCombo" name="cmbAddresses"/>
</item>
<item row="2" column="0">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Destination Address</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTableWidget" name="balancesTable">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
</widget>
</item>
<item row="5" column="0">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AddressCombo</class>
<extends>QComboBox</extends>
<header>addresscombo.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NullifierMigrationDialog</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>NullifierMigrationDialog</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>
1 change: 1 addition & 0 deletions src/precompiled.h
Expand Up @@ -27,6 +27,7 @@
#include <QStandardItemModel>
#include <QStandardItem>
#include <QScrollBar>
#include <QTableWidgetItem>
#include <QPainter>
#include <QMovie>
#include <QPair>
Expand Down
23 changes: 23 additions & 0 deletions src/rpc.cpp
Expand Up @@ -906,6 +906,29 @@ void RPC::addNewTxToWatch(const QString& newOpid, WatchedTx wtx) {
watchTxStatus();
}

/**
* Execute a transaction with the standard UI. i.e., standard status bar message and standard error
* handling
*/
void RPC::executeStandardUITransaction(Tx tx) {
executeTransaction(tx,
[=] (QString opid) {
ui->statusBar->showMessage(QObject::tr("Computing Tx: ") % opid);
},
[=] (QString, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000);

if (!opid.isEmpty())
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr;

QMessageBox::critical(main, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
}
);
}


// Execute a transaction!
void RPC::executeTransaction(Tx tx,
Expand Down
2 changes: 2 additions & 0 deletions src/rpc.h
Expand Up @@ -49,6 +49,8 @@ class RPC
void refreshZECPrice();
void getZboardTopics(std::function<void(QMap<QString, QString>)> cb);

void executeStandardUITransaction(Tx tx);

void executeTransaction(Tx tx,
const std::function<void(QString opid)> submitted,
const std::function<void(QString opid, QString txid)> computed,
Expand Down
17 changes: 1 addition & 16 deletions src/sendtab.cpp
Expand Up @@ -673,22 +673,7 @@ void MainWindow::sendButton() {
// Show a dialog to confirm the Tx
if (confirmTx(tx)) {
// And send the Tx
rpc->executeTransaction(tx,
[=] (QString opid) {
ui->statusBar->showMessage(tr("Computing Tx: ") % opid);
},
[=] (QString, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000);

if (!opid.isEmpty())
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr;

QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
}
);
rpc->executeStandardUITransaction(tx);
}
}

Expand Down
1 change: 1 addition & 0 deletions yecwallet.pro
Expand Up @@ -90,6 +90,7 @@ HEADERS += \

FORMS += \
src/mainwindow.ui \
src/nullifiermigration.ui \
src/settings.ui \
src/about.ui \
src/confirm.ui \
Expand Down