From eac29cd0b6ff777aa90aacf069a59fe0e13c692a Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 13 Oct 2020 00:01:06 +0200 Subject: [PATCH] Import wallet from keys (view-only) Signed-off-by: dsc --- src/appcontext.cpp | 49 ++++++++++- src/appcontext.h | 4 + src/dialog/viewonlydialog.cpp | 59 ++++++++++++++ src/dialog/viewonlydialog.h | 32 ++++++++ src/dialog/viewonlydialog.ui | 139 ++++++++++++++++++++++++++++++++ src/mainwindow.cpp | 19 +++-- src/mainwindow.h | 1 + src/mainwindow.ui | 18 ++++- src/utils/seeds.h | 14 ++-- src/wizard/createwallet.cpp | 10 ++- src/wizard/createwalletseed.cpp | 2 +- src/wizard/menu.cpp | 2 + src/wizard/menu.ui | 7 ++ src/wizard/restorewallet.cpp | 4 +- src/wizard/viewonlywallet.cpp | 106 ++++++++++++++++++++++++ src/wizard/viewonlywallet.h | 36 +++++++++ src/wizard/viewonlywallet.ui | 119 +++++++++++++++++++++++++++ src/wizard/walletwizard.cpp | 19 ++++- src/wizard/walletwizard.h | 2 +- 19 files changed, 619 insertions(+), 23 deletions(-) create mode 100644 src/dialog/viewonlydialog.cpp create mode 100644 src/dialog/viewonlydialog.h create mode 100644 src/dialog/viewonlydialog.ui create mode 100644 src/wizard/viewonlywallet.cpp create mode 100644 src/wizard/viewonlywallet.h create mode 100644 src/wizard/viewonlywallet.ui diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 77fa897..e60282c 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -341,6 +341,13 @@ void AppContext::onWalletOpened(Wallet *wallet) { // force trigger preferredFiat signal for history model this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString()); + + // (window) title + QFileInfo fileInfo(this->walletPath); + auto title = QString("Feather - [%1]").arg(fileInfo.fileName()); + if(this->currentWallet->viewOnly()) + title += " [view-only]"; + emit setTitle(title); } void AppContext::onWSMessage(const QJsonObject &msg) { @@ -541,6 +548,43 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri return; } + this->createWalletFinish(password); +} + +void AppContext::createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight) { + if(Utils::fileExists(path)) { + auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path); + qCritical() << err; + emit walletCreatedError(err); + return; + } + + if(!this->walletManager->addressValid(address, this->networkType)) { + auto err = QString("Failed to create wallet. Invalid address provided.").arg(path); + qCritical() << err; + emit walletCreatedError(err); + return; + } + + if(!this->walletManager->keyValid(viewkey, address, true, this->networkType)) { + auto err = QString("Failed to create wallet. Invalid viewkey provided.").arg(path); + qCritical() << err; + emit walletCreatedError(err); + return; + } + + if(!spendkey.isEmpty() && !this->walletManager->keyValid(spendkey, address, false, this->networkType)) { + auto err = QString("Failed to create wallet. Invalid spendkey provided.").arg(path); + qCritical() << err; + emit walletCreatedError(err); + return; + } + + this->currentWallet = this->walletManager->createWalletFromKeys(path, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight); + this->createWalletFinish(password); +} + +void AppContext::createWalletFinish(const QString &password) { this->currentWallet->setPassword(password); this->currentWallet->store(); this->walletPassword = password; @@ -614,8 +658,9 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) { } void AppContext::donateBeg() { - if(this->networkType != NetworkType::Type::MAINNET) - return; + if(this->currentWallet == nullptr) return; + if(this->networkType != NetworkType::Type::MAINNET) return; + if(this->currentWallet->viewOnly()) return; auto donationCounter = config()->get(Config::donateBeg).toInt(); if(donationCounter == -1) diff --git a/src/appcontext.h b/src/appcontext.h index b4bf04a..577738a 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -68,6 +68,7 @@ public: PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; quint32 tx_mixin = static_cast(10); static constexpr const double cdiv = 1e12; + QString seedLanguage = "English"; // 14 word `monero-seed` only has English QNetworkAccessManager *network; QNetworkAccessManager *networkClearnet; @@ -89,6 +90,8 @@ public: WalletManager *walletManager; Wallet *currentWallet = nullptr; void createWallet(FeatherSeed seed, const QString &path, const QString &password); + void createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight); + void createWalletFinish(const QString &password); void syncStatusUpdated(quint64 height, quint64 target); void updateBalance(); void initTor(); @@ -160,6 +163,7 @@ signals: void initiateTransaction(); void endTransaction(); void walletClosing(); + void setTitle(const QString &title); // set window title private: void sorry(); diff --git a/src/dialog/viewonlydialog.cpp b/src/dialog/viewonlydialog.cpp new file mode 100644 index 0000000..b66e12b --- /dev/null +++ b/src/dialog/viewonlydialog.cpp @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#include +#include +#include + +#include "viewonlydialog.h" +#include "ui_viewonlydialog.h" + +ViewOnlyDialog::ViewOnlyDialog(AppContext *ctx, QWidget *parent) + : QDialog(parent) + , ui(new Ui::ViewOnlyDialog), m_ctx(ctx) +{ + ui->setupUi(this); + + ui->label_restoreHeight->setText(QString::number(ctx->currentWallet->getWalletCreationHeight())); + ui->label_primaryAddress->setText(ctx->currentWallet->address(0, 0)); + ui->label_secretViewKey->setText(ctx->currentWallet->getSecretViewKey()); + + connect(ui->btn_Copy, &QPushButton::clicked, this, &ViewOnlyDialog::copyToClipboad); + connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet); + + ui->btn_Save->setEnabled(!m_ctx->currentWallet->viewOnly()); + this->adjustSize(); +} + +void ViewOnlyDialog::onWriteViewOnlyWallet(){ + QString fn = QFileDialog::getSaveFileName(this, "Save .keys wallet file", QDir::homePath(), "Monero wallet (*.keys)"); + if(fn.isEmpty()) return; + if(!fn.endsWith(".keys")) fn += ".keys"; + + QString passwd; + QInputDialog passwordDialog(this); + passwordDialog.setInputMode(QInputDialog::TextInput); + passwordDialog.setTextEchoMode(QLineEdit::Password); + passwordDialog.setWindowTitle("View-Only wallet password"); + passwordDialog.setLabelText("Protect this view-only wallet with a password?"); + passwordDialog.resize(300, 100); + if((bool)passwordDialog.exec()) + passwd = passwordDialog.textValue(); + + m_ctx->currentWallet->createViewOnly(fn, passwd); + + QMessageBox::information(this, "Information", "View-only wallet successfully written to disk."); +} + +void ViewOnlyDialog::copyToClipboad() { + QString text = ""; + text += QString("Address: %1\n").arg(ui->label_primaryAddress->text()); + text += QString("Secret view key: %1\n").arg(ui->label_secretViewKey->text()); + text += QString("Restore height: %1\n").arg(ui->label_restoreHeight->text()); + Utils::copyToClipboard(text); +} + +ViewOnlyDialog::~ViewOnlyDialog() +{ + delete ui; +} diff --git a/src/dialog/viewonlydialog.h b/src/dialog/viewonlydialog.h new file mode 100644 index 0000000..555f76b --- /dev/null +++ b/src/dialog/viewonlydialog.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#ifndef FEATHER_VIEWONLYDIALOG_H +#define FEATHER_VIEWONLYDIALOG_H + +#include +#include "appcontext.h" + +namespace Ui { + class ViewOnlyDialog; +} + +class ViewOnlyDialog : public QDialog +{ +Q_OBJECT + +public: + explicit ViewOnlyDialog(AppContext *ctx, QWidget *parent = nullptr); + ~ViewOnlyDialog() override; + +private slots: + void onWriteViewOnlyWallet(); + +private: + Ui::ViewOnlyDialog *ui; + AppContext *m_ctx = nullptr; + void copyToClipboad(); +}; + + +#endif //FEATHER_KEYSDIALOG_H diff --git a/src/dialog/viewonlydialog.ui b/src/dialog/viewonlydialog.ui new file mode 100644 index 0000000..2cdcdb8 --- /dev/null +++ b/src/dialog/viewonlydialog.ui @@ -0,0 +1,139 @@ + + + ViewOnlyDialog + + + + 0 + 0 + 659 + 254 + + + + View-Only + + + + + + Restore height + + + + + + TextLabel + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Primary address + + + + + + TextLabel + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Secret view key + + + + + + TextLabel + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + Copy + + + + + + + Create view-only wallet + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + ViewOnlyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ViewOnlyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 61ba1a4..8217ae7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -16,6 +16,7 @@ #include "dialog/debuginfodialog.h" #include "dialog/walletinfodialog.h" #include "dialog/torinfodialog.h" +#include "dialog/viewonlydialog.h" #include "utils/utils.h" #include "utils/config.h" #include "components.h" @@ -325,6 +326,10 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : connect(m_ctx, &AppContext::walletClosing, [=]{ ui->tabWidget->setCurrentIndex(0); }); + + // window title + connect(m_ctx, &AppContext::setTitle, this, &QMainWindow::setWindowTitle); + // setup some UI this->initMain(); this->initWidgets(); @@ -389,6 +394,7 @@ void MainWindow::initMenu() { connect(ui->actionSeed, &QAction::triggered, this, &MainWindow::showSeedDialog); connect(ui->actionPassword, &QAction::triggered, this, &MainWindow::showPasswordDialog); connect(ui->actionKeys, &QAction::triggered, this, &MainWindow::showKeysDialog); + connect(ui->actionViewOnly, &QAction::triggered, this, &MainWindow::showViewOnlyDialog); connect(ui->actionStore_wallet, &QAction::triggered, [&]{ m_ctx->currentWallet->store(); }); @@ -412,7 +418,8 @@ void MainWindow::initMenu() { connect(ui->actionExport_CSV, &QAction::triggered, [=]{ if(m_ctx->currentWallet == nullptr) return; QString fn = QFileDialog::getSaveFileName(this, "Save CSV file", QDir::homePath(), "CSV (*.csv)"); - if(!fn.startsWith(".csv")) fn += ".csv"; + if(fn.isEmpty()) return; + if(!fn.endsWith(".csv")) fn += ".csv"; m_ctx->currentWallet->history()->writeCSV(fn); Utils::showMessageBox("CSV export", QString("Transaction history exported to %1").arg(fn), false); }); @@ -543,10 +550,6 @@ void MainWindow::onWalletOpened() { else m_statusLabelStatus->setText("Wallet opened - Searching for node"); - // window title as wallet name - QFileInfo fileInfo(m_ctx->walletPath); - this->setWindowTitle(QString("Feather - [%1]").arg(fileInfo.fileName())); - connect(m_ctx->currentWallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged); // receive page @@ -850,6 +853,12 @@ void MainWindow::showKeysDialog() { dialog->deleteLater(); } +void MainWindow::showViewOnlyDialog() { + auto *dialog = new ViewOnlyDialog(m_ctx, this); + dialog->exec(); + dialog->deleteLater(); +} + void MainWindow::menuTorClicked() { auto *dialog = new TorInfoDialog(m_ctx, this); diff --git a/src/mainwindow.h b/src/mainwindow.h index 2d7d791..5633826 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -79,6 +79,7 @@ public slots: void showConnectionStatusDialog(); void showPasswordDialog(); void showKeysDialog(); + void showViewOnlyDialog(); void donateButtonClicked(); void showCalcWindow(); void showSendTab(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 1cc326e..3019609 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -291,7 +291,7 @@ 0 0 894 - 30 + 22 @@ -335,6 +335,7 @@ + @@ -518,6 +519,21 @@ Debug info + + + Details + + + + + Export wallet file + + + + + View-Only + + diff --git a/src/utils/seeds.h b/src/utils/seeds.h index c3805f1..aedc518 100644 --- a/src/utils/seeds.h +++ b/src/utils/seeds.h @@ -84,16 +84,16 @@ struct FeatherSeed { time_t time = 0; unsigned int restoreHeight = 0; RestoreHeightLookup *lookup = nullptr; - QString language = "English"; + QString language; std::string coinName; - explicit FeatherSeed(RestoreHeightLookup *lookup, const std::string &coinName = "monero") : lookup(lookup), coinName(coinName) {} + explicit FeatherSeed(RestoreHeightLookup *lookup, const std::string &coinName = "monero", const QString &language = "English") : lookup(lookup), coinName(coinName), language(language) {} static FeatherSeed fromSeed(RestoreHeightLookup *lookup, const std::string &coinName, + const QString &seedLanguage, const std::string &mnemonicSeed) { - auto rtn = FeatherSeed(lookup, coinName); - rtn.coinName = coinName; + auto rtn = FeatherSeed(lookup, coinName, seedLanguage); rtn.lookup = lookup; rtn.mnemonicSeed = QString::fromStdString(mnemonicSeed); @@ -108,8 +108,8 @@ struct FeatherSeed { return rtn; } - static FeatherSeed generate(RestoreHeightLookup *lookup, const std::string &coinName) { - auto rtn = FeatherSeed(lookup, coinName); + static FeatherSeed generate(RestoreHeightLookup *lookup, const std::string &coinName, const QString &language) { + auto rtn = FeatherSeed(lookup, coinName, language); time_t _time = std::time(nullptr); monero_seed seed(_time, coinName); @@ -130,7 +130,7 @@ struct FeatherSeed { if(this->lookup == nullptr) return wallet; if(this->mnemonicSeed.split(" ").count() == 14) { if(this->spendKey.isEmpty()) { - auto _seed = FeatherSeed::fromSeed(this->lookup, this->coinName, this->mnemonicSeed.toStdString()); + auto _seed = FeatherSeed::fromSeed(this->lookup, this->coinName, this->language, this->mnemonicSeed.toStdString()); _seed.setRestoreHeight(); this->time = _seed.time; this->restoreHeight = _seed.restoreHeight; diff --git a/src/wizard/createwallet.cpp b/src/wizard/createwallet.cpp index b9e42fb..a5b223a 100644 --- a/src/wizard/createwallet.cpp +++ b/src/wizard/createwallet.cpp @@ -82,7 +82,12 @@ bool CreateWalletPage::validateWidgets(){ int CreateWalletPage::nextId() const { auto restoredSeed = this->field("mnemonicRestoredSeed").toString(); - return restoredSeed.isEmpty() ? WalletWizard::Page_CreateWalletSeed : -1; + auto restoredViewOnlyKey = this->field("viewOnlyViewKey").toString(); + + if(!restoredSeed.isEmpty() || !restoredViewOnlyKey.isEmpty()) + return -1; + + return WalletWizard::Page_CreateWalletSeed; } bool CreateWalletPage::validatePage() { @@ -93,6 +98,7 @@ bool CreateWalletPage::validatePage() { ui->walletName->setStyleSheet(""); auto restoredSeed = this->field("mnemonicRestoredSeed").toString(); - if(!restoredSeed.isEmpty()) emit createWallet(); + auto restoredViewOnlyKey = this->field("viewOnlyViewKey").toString(); + if(!restoredSeed.isEmpty() || !restoredViewOnlyKey.isEmpty()) emit createWallet(); return true; } diff --git a/src/wizard/createwalletseed.cpp b/src/wizard/createwalletseed.cpp index 6264a6a..1bc8940 100644 --- a/src/wizard/createwalletseed.cpp +++ b/src/wizard/createwalletseed.cpp @@ -37,7 +37,7 @@ CreateWalletSeedPage::CreateWalletSeedPage(AppContext *ctx, QWidget *parent) : void CreateWalletSeedPage::seedRoulette(int count) { count += 1; if(count > m_rouletteSpin) return; - auto seed = FeatherSeed::generate(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString()); + auto seed = FeatherSeed::generate(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage); m_mnemonic = seed.mnemonicSeed; m_restoreHeight = seed.restoreHeight; diff --git a/src/wizard/menu.cpp b/src/wizard/menu.cpp index c927ce3..fc16cba 100644 --- a/src/wizard/menu.cpp +++ b/src/wizard/menu.cpp @@ -28,6 +28,8 @@ int MenuPage::nextId() const { return WalletWizard::Page_CreateWallet; if(ui->radioSeed->isChecked()) return WalletWizard::Page_Restore; + if(ui->radioViewOnly->isChecked()) + return WalletWizard::Page_ViewOnly; return 0; } diff --git a/src/wizard/menu.ui b/src/wizard/menu.ui index 2c6180e..2ed69e2 100644 --- a/src/wizard/menu.ui +++ b/src/wizard/menu.ui @@ -48,6 +48,13 @@ + + + + Import from keys + + + diff --git a/src/wizard/restorewallet.cpp b/src/wizard/restorewallet.cpp index 0f37a05..385dc1b 100644 --- a/src/wizard/restorewallet.cpp +++ b/src/wizard/restorewallet.cpp @@ -130,7 +130,7 @@ bool RestorePage::validatePage() { } } - auto _seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], "monero", seed.toStdString()); + auto _seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage, seed.toStdString()); restoreHeight = _seed.restoreHeight; this->setField("restoreHeight", restoreHeight); @@ -153,7 +153,7 @@ bool RestorePage::validatePage() { } } - auto _seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], "monero", seed.toStdString()); + auto _seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage, seed.toStdString()); _seed.setRestoreHeight(restoreHeight); this->setField("restoreHeight", restoreHeight); this->setField("mnemonicSeed", seed); diff --git a/src/wizard/viewonlywallet.cpp b/src/wizard/viewonlywallet.cpp new file mode 100644 index 0000000..d2ddce0 --- /dev/null +++ b/src/wizard/viewonlywallet.cpp @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#include "wizard/viewonlywallet.h" +#include "wizard/walletwizard.h" +#include "ui_viewonlywallet.h" + +#include +#include +#include +#include +#include + +#include // tevador 14 word + +#include "libwalletqt/WalletManager.h" + +ViewOnlyPage::ViewOnlyPage(AppContext *ctx, QWidget *parent) : + QWizardPage(parent), + ui(new Ui::ViewOnlyPage), + m_ctx(ctx) { + ui->setupUi(this); + this->setTitle("Import view only wallet"); + ui->label_errorString->hide(); + + QFont f("feather"); + f.setStyleHint(QFont::Monospace); + + auto *viewOnlyViewKeyDummy = new QLineEdit(this); + viewOnlyViewKeyDummy->setVisible(false); + auto *viewOnlySpendKeyDummy = new QLineEdit(this); + viewOnlySpendKeyDummy->setVisible(false); + auto *viewOnlyAddressDummy = new QLineEdit(this); + viewOnlyAddressDummy->setVisible(false); + auto *restoreHeightDummy = new QLineEdit(this); + restoreHeightDummy->setVisible(false); + + this->registerField("viewOnlySpendKey", viewOnlySpendKeyDummy); + this->registerField("viewOnlyViewKey", viewOnlyViewKeyDummy); + this->registerField("viewOnlyAddress", viewOnlyAddressDummy); + this->registerField("viewOnlyHeight", restoreHeightDummy); + +#ifndef QT_NO_CURSOR + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + QGuiApplication::restoreOverrideCursor(); +#endif + + if(m_ctx->networkType == NetworkType::Type::TESTNET) { + ui->restoreHeightWidget->hideSlider(); + } else { + // load restoreHeight lookup db + ui->restoreHeightWidget->initRestoreHeights(m_ctx->restoreHeights[m_ctx->networkType]); + } + + if(m_ctx->networkType == NetworkType::Type::MAINNET) { + ui->lineEdit_address->setPlaceholderText("4..."); + } else if (m_ctx->networkType == NetworkType::Type::STAGENET) { + ui->lineEdit_address->setPlaceholderText("5..."); + } +} + +int ViewOnlyPage::nextId() const { + return WalletWizard::Page_CreateWallet; +} + +void ViewOnlyPage::cleanupPage() const {} + +bool ViewOnlyPage::validatePage() { + auto errStyle = "QLineEdit{border: 1px solid red;}"; + + ui->lineEdit_address->setStyleSheet(""); + ui->lineEdit_viewkey->setStyleSheet(""); + ui->label_errorString->hide(); + + unsigned int restoreHeight = ui->restoreHeightWidget->getHeight(); + auto spendkey = ui->lineEdit_spendkey->text().trimmed(); + auto viewkey = ui->lineEdit_viewkey->text().trimmed(); + auto address = ui->lineEdit_address->text().trimmed(); + + if(!m_ctx->walletManager->addressValid(address, m_ctx->networkType)){ + ui->label_errorString->show(); + ui->label_errorString->setText("Invalid address."); + ui->lineEdit_address->setStyleSheet(errStyle); + return false; + } + + if(!m_ctx->walletManager->keyValid(viewkey, address, true, m_ctx->networkType)) { + ui->label_errorString->show(); + ui->label_errorString->setText("Invalid key."); + ui->lineEdit_viewkey->setStyleSheet(errStyle); + return false; + } + + if(!spendkey.isEmpty() && !m_ctx->walletManager->keyValid(spendkey, address, false, m_ctx->networkType)) { + ui->label_errorString->show(); + ui->label_errorString->setText("Invalid key."); + ui->lineEdit_viewkey->setStyleSheet(errStyle); + return false; + } + + this->setField("viewOnlyViewKey", viewkey); + this->setField("viewOnlySpendKey", spendkey); + this->setField("viewOnlyAddress", address); + this->setField("viewOnlyHeight", restoreHeight); + return true; +} diff --git a/src/wizard/viewonlywallet.h b/src/wizard/viewonlywallet.h new file mode 100644 index 0000000..1fbcff4 --- /dev/null +++ b/src/wizard/viewonlywallet.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#ifndef FEATHER_WIZARDVIEWONLY_H +#define FEATHER_WIZARDVIEWONLY_H + +#include +#include +#include +#include +#include +#include + +#include "appcontext.h" + +namespace Ui { + class ViewOnlyPage; +} + +class ViewOnlyPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit ViewOnlyPage(AppContext *ctx, QWidget *parent = nullptr); + bool validatePage() override; + int nextId() const override; + void cleanupPage() const; + +private: + AppContext *m_ctx; + QLabel *topLabel; + Ui::ViewOnlyPage *ui; +}; + +#endif diff --git a/src/wizard/viewonlywallet.ui b/src/wizard/viewonlywallet.ui new file mode 100644 index 0000000..426d78c --- /dev/null +++ b/src/wizard/viewonlywallet.ui @@ -0,0 +1,119 @@ + + + ViewOnlyPage + + + + 0 + 0 + 502 + 506 + + + + ViewOnlyPage + + + + + + Standard address + + + + + + + + + + Secret view key + + + + + + + + + + Secret spend key (optional) + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + You may specify the "restore height". This is the moment the wallet was created, expressed through a blockheight (number). This speeds up wallet refreshes. If you don't know, leave it empty. + + + true + + + + + + + + + + errorString + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + RestoreHeightWidget + QWidget +
widgets/restoreheightwidget.h
+ 1 +
+
+ + +
diff --git a/src/wizard/walletwizard.cpp b/src/wizard/walletwizard.cpp index d3b8538..82bbf61 100644 --- a/src/wizard/walletwizard.cpp +++ b/src/wizard/walletwizard.cpp @@ -9,9 +9,11 @@ #include "wizard/network.h" #include "wizard/createwalletseed.h" #include "wizard/restorewallet.h" +#include "wizard/viewonlywallet.h" #include #include +#include #include #include #include @@ -29,6 +31,7 @@ WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidge setPage(Page_CreateWalletSeed, createWalletSeed); setPage(Page_Network, new NetworkPage(m_ctx, this)); setPage(Page_Restore, new RestorePage(m_ctx, this)); + setPage(Page_ViewOnly, new ViewOnlyPage(m_ctx, this)); if(config()->get(Config::firstRun).toUInt()) setStartId(Page_Network); @@ -58,9 +61,21 @@ void WalletWizard::createWallet() { const auto walletPath = this->field("walletPath").toString(); const auto walletPasswd = this->field("walletPasswd").toString(); auto restoreHeight = this->field("restoreHeight").toUInt(); + auto viewKey = this->field("viewOnlyViewKey").toString(); + auto spendKey = this->field("viewOnlySpendKey").toString(); + auto viewAddress = this->field("viewOnlyAddress").toString(); - auto seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), mnemonicSeed.toStdString()); + if(!viewKey.isEmpty() && !viewAddress.isEmpty()) { + auto viewHeight = this->field("viewOnlyHeight").toUInt(); + m_ctx->createWalletViewOnly(walletPath, + walletPasswd, + viewAddress, + viewKey, spendKey, viewHeight); + return; + } + + auto seed = FeatherSeed::fromSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName.toStdString(), m_ctx->seedLanguage, mnemonicSeed.toStdString()); if(restoreHeight > 0) seed.setRestoreHeight(restoreHeight); m_ctx->createWallet(seed, walletPath, walletPasswd); -} \ No newline at end of file +} diff --git a/src/wizard/walletwizard.h b/src/wizard/walletwizard.h index 1d54ffd..589ac99 100644 --- a/src/wizard/walletwizard.h +++ b/src/wizard/walletwizard.h @@ -17,7 +17,7 @@ class WalletWizard : public QWizard Q_OBJECT public: - enum Page { Page_Menu, Page_CreateWallet, Page_CreateWalletSeed, Page_OpenWallet, Page_Network, Page_Restore }; + enum Page { Page_Menu, Page_CreateWallet, Page_CreateWalletSeed, Page_OpenWallet, Page_Network, Page_Restore, Page_ViewOnly }; explicit WalletWizard(AppContext *ctx, WalletWizard::Page startPage = WalletWizard::Page::Page_Menu, QWidget *parent = nullptr); signals: