diff --git a/CMakeLists.txt b/CMakeLists.txt index a1a3ac0..e51abf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ if(DEBUG) set(CMAKE_VERBOSE_MAKEFILE ON) endif() -set(MONERO_HEAD "aa0f58570d412cf02dd325e567bbc9fa093df16c") +set(MONERO_HEAD "e175e02b9b8d289bccab3ff0f3fd70c4dbf8c71f") set(BUILD_GUI_DEPS ON) set(ARCH "x86-64") set(BUILD_64 ON) diff --git a/monero b/monero index aa0f585..e175e02 160000 --- a/monero +++ b/monero @@ -1 +1 @@ -Subproject commit aa0f58570d412cf02dd325e567bbc9fa093df16c +Subproject commit e175e02b9b8d289bccab3ff0f3fd70c4dbf8c71f diff --git a/src/appcontext.cpp b/src/appcontext.cpp index cc0da0e..afa53d0 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -26,84 +26,39 @@ AppContext::AppContext(QCommandLineParser *cmdargs) { this->networkClearnet = new QNetworkAccessManager(); this->cmdargs = cmdargs; -#if defined(Q_OS_MAC) - this->isTorSocks = qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0; -#elif defined(Q_OS_LINUX) - this->isTorSocks = qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0; -#elif defined(Q_OS_WIN) - this->isTorSocks = false; -#endif - + this->isTorSocks = Utils::isTorsocks(); this->isTails = TailsOS::detect(); this->isWhonix = WhonixOS::detect(); - //Paths - this->configRoot = QDir::homePath(); - if (isTails) { // #if defined(PORTABLE) - QString portablePath = []{ - QString appImagePath = qgetenv("APPIMAGE"); - if (appImagePath.isEmpty()) { - qDebug() << "Not an appimage, using currentPath()"; - return QDir::currentPath() + "/.feather"; - } + // ----------------- Setup Paths ----------------- - QFileInfo appImageDir(appImagePath); - return appImageDir.absoluteDir().path() + "/.feather"; - }(); - - - if (QDir().mkpath(portablePath)) { - this->configRoot = portablePath; - } else { - qCritical() << "Unable to create portable directory: " << portablePath; - } - } - - this->accountName = Utils::getUnixAccountName(); - this->homeDir = QDir::homePath(); + QString configDir = Config::defaultConfigDir().path(); + createConfigDirectory(configDir); QString walletDir = config()->get(Config::walletDirectory).toString(); if (walletDir.isEmpty()) { -#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) - this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot); - this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot); -#elif defined(Q_OS_WIN) - this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero"; - this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); -#endif - } else { - this->defaultWalletDir = walletDir; - this->defaultWalletDirRoot = walletDir; + walletDir = Utils::defaultWalletDir(); } - - // Create wallet dirs + this->defaultWalletDir = walletDir; if (!QDir().mkpath(defaultWalletDir)) qCritical() << "Unable to create dir: " << defaultWalletDir; - this->configDirectory = QString("%1/.config/feather/").arg(this->configRoot); -#if defined(Q_OS_UNIX) - if(!this->configDirectory.endsWith('/')) - this->configDirectory = QString("%1/").arg(this->configDirectory); -#endif + // ----------------- Network Type ----------------- - // Config - createConfigDirectory(this->configDirectory); - - if(this->cmdargs->isSet("stagenet")) + if (this->cmdargs->isSet("stagenet")) this->networkType = NetworkType::STAGENET; - else if(this->cmdargs->isSet("testnet")) + else if (this->cmdargs->isSet("testnet")) this->networkType = NetworkType::TESTNET; else this->networkType = NetworkType::MAINNET; -// auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt(); -// AppContext::nodeSource = static_cast(nodeSourceUInt); + this->nodes = new Nodes(this, this->networkClearnet); connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged); connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes); // Tor & socks proxy - this->ws = new WSClient(this, m_wsUrl); + this->ws = new WSClient(this, globals::websocketUrl); connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage); // Store the wallet every 2 minutes @@ -117,7 +72,7 @@ AppContext::AppContext(QCommandLineParser *cmdargs) { // price history lookup auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey(); - AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, this->configDirectory); + AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, configDir); connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase); connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){ QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8(); @@ -133,18 +88,18 @@ AppContext::AppContext(QCommandLineParser *cmdargs) { // XMRig #ifdef HAS_XMRIG - this->XMRig = new XmRig(this->configDirectory, this); + this->XMRig = new XmRig(configDir, this); this->XMRig->prepare(); #endif this->walletManager = WalletManager::instance(); - QString logPath = QString("%1/daemon.log").arg(configDirectory); + QString logPath = QString("%1/daemon.log").arg(configDir); Monero::Utils::onStartup(); Monero::Wallet::init("", "feather", logPath.toStdString(), true); bool logLevelFromEnv; int logLevel = qEnvironmentVariableIntValue("MONERO_LOG_LEVEL", &logLevelFromEnv); - if(this->cmdargs->isSet("quiet")) + if (this->cmdargs->isSet("quiet")) this->walletManager->setLogLevel(-1); else if (logLevelFromEnv && logLevel >= 0 && logLevel <= Monero::WalletManagerFactory::LogLevel_Max) Monero::WalletManagerFactory::setLogLevel(logLevel); @@ -159,11 +114,12 @@ void AppContext::initTor() { this->tor = new Tor(this, this); this->tor->start(); - if (!(isWhonix)) { + if (!(isWhonix) && !(isTorSocks)) { this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); this->network->setProxy(*networkProxy); - if (m_wsUrl.host().endsWith(".onion")) + if (globals::websocketUrl.host().endsWith(".onion")) { this->ws->webSocket.setProxy(*networkProxy); + } } } @@ -218,9 +174,9 @@ void AppContext::onCreateTransaction(const QString &address, quint64 amount, con qDebug() << "creating tx"; if (all) - this->currentWallet->createTransactionAllAsync(address, "", this->tx_mixin, this->tx_priority); + this->currentWallet->createTransactionAllAsync(address, "", globals::mixin, this->tx_priority); else - this->currentWallet->createTransactionAsync(address, "", amount, this->tx_mixin, this->tx_priority); + this->currentWallet->createTransactionAsync(address, "", amount, globals::mixin, this->tx_priority); emit initiateTransaction(); } @@ -335,7 +291,6 @@ void AppContext::onWalletOpened(Wallet *wallet) { this->refreshed = false; this->currentWallet = wallet; this->walletPath = this->currentWallet->path() + ".keys"; - this->walletViewOnly = this->currentWallet->viewOnly(); config()->set(Config::walletPath, this->walletPath); connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent); @@ -368,7 +323,7 @@ void AppContext::onWalletOpened(Wallet *wallet) { void AppContext::setWindowTitle(bool mining) { QFileInfo fileInfo(this->walletPath); auto title = QString("Feather - [%1]").arg(fileInfo.fileName()); - if(this->walletViewOnly) + if(this->currentWallet && this->currentWallet->viewOnly()) title += " [view-only]"; if(mining) title += " [mining]"; @@ -536,7 +491,7 @@ void AppContext::createConfigDirectory(const QString &dir) { } } -void AppContext::createWallet(FeatherSeed seed, const QString &path, const QString &password) { +void AppContext::createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset) { if(Utils::fileExists(path)) { auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path); qCritical() << err; @@ -551,11 +506,12 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri Wallet *wallet = nullptr; if (seed.seedType == SeedType::TEVADOR) { - wallet = this->walletManager->createDeterministicWalletFromSpendKey(path, password, seed.language, this->networkType, seed.spendKey, seed.restoreHeight, this->kdfRounds); + wallet = this->walletManager->createDeterministicWalletFromSpendKey(path, password, seed.language, this->networkType, seed.spendKey, seed.restoreHeight, globals::kdfRounds, seedOffset); wallet->setCacheAttribute("feather.seed", seed.mnemonic.join(" ")); + wallet->setCacheAttribute("feather.seedoffset", seedOffset); } if (seed.seedType == SeedType::MONERO) { - wallet = this->walletManager->recoveryWallet(path, password, seed.mnemonic.join(" "), "", this->networkType, seed.restoreHeight, this->kdfRounds); + wallet = this->walletManager->recoveryWallet(path, password, seed.mnemonic.join(" "), seedOffset, this->networkType, seed.restoreHeight, globals::kdfRounds); } this->currentWallet = wallet; @@ -567,7 +523,7 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri this->createWalletFinish(password); } -void AppContext::createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight) { +void AppContext::createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic) { if(Utils::fileExists(path)) { auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path); qCritical() << err; @@ -674,17 +630,18 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) { } void AppContext::donateBeg() { - if(this->currentWallet == nullptr) return; - if(this->networkType != NetworkType::Type::MAINNET) return; - if(this->currentWallet->viewOnly()) 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) return; // previously donated donationCounter += 1; - if (donationCounter % m_donationBoundary == 0) + if (donationCounter % globals::donationBoundary == 0) { emit donationNag(); + } config()->set(Config::donateBeg, donationCounter); } @@ -763,7 +720,7 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector &address) { for (auto &addr : address) { - if (addr == this->donationAddress) { + if (addr == globals::donationAddress) { this->donationSending = true; } } diff --git a/src/appcontext.h b/src/appcontext.h index 46d65d1..5e185e4 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -37,41 +37,25 @@ Q_OBJECT public: explicit AppContext(QCommandLineParser *cmdargs); ~AppContext() override; - bool isTails = false; - bool isWhonix = false; - bool isDebug = false; - - // Donation config - const QString donationAddress = "47ntfT2Z5384zku39pTM6hGcnLnvpRYW2Azm87GiAAH2bcTidtq278TL6HmwyL8yjMeERqGEBs3cqC8vvHPJd1cWQrGC65f"; - const int donationAmount = 25; // euro - bool donationSending = false; QCommandLineParser *cmdargs; - QString coinName = "monero"; + bool isTails = false; + bool isWhonix = false; bool isTorSocks = false; - QString homeDir; - QString accountName; - QString configRoot; - QString configDirectory; + + bool donationSending = false; + QString defaultWalletDir; - QString defaultWalletDirRoot; QString tmpTxDescription; QString walletPath; QString walletPassword = ""; - bool walletViewOnly = false; NetworkType::Type networkType; - QString applicationPath; - - static void createConfigDirectory(const QString &dir) ; - QMap heights; QMap restoreHeights; - const quint64 kdfRounds = 1; PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; - quint32 tx_mixin = static_cast(10); QString seedLanguage = "English"; // 14 word `monero-seed` only has English QNetworkAccessManager *network; @@ -88,12 +72,14 @@ public: static QMap txCache; static TxFiatHistory *txFiatHistory; + static void createConfigDirectory(const QString &dir); + // libwalletqt bool refreshed = false; 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 createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset = ""); + void createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic = false); void createWalletFinish(const QString &password); void syncStatusUpdated(quint64 height, quint64 target); void updateBalance(); @@ -176,9 +162,7 @@ signals: void setTitle(const QString &title); // set window title private: - const int m_donationBoundary = 15; QTimer m_storeTimer; - QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws")); }; #endif //FEATHER_APPCONTEXT_H diff --git a/src/dialog/debuginfodialog.ui b/src/dialog/debuginfodialog.ui index 9348dba..e650475 100644 --- a/src/dialog/debuginfodialog.ui +++ b/src/dialog/debuginfodialog.ui @@ -321,6 +321,9 @@ TextLabel + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + diff --git a/src/dialog/seeddialog.cpp b/src/dialog/seeddialog.cpp index 9232981..63f46a2 100644 --- a/src/dialog/seeddialog.cpp +++ b/src/dialog/seeddialog.cpp @@ -13,8 +13,9 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent) ui->label_restoreHeight->setText(QString::number(wallet->getWalletCreationHeight())); + QString seedOffset = wallet->getCacheAttribute("feather.seedoffset"); QString seed_14_words = wallet->getCacheAttribute("feather.seed"); - QString seed_25_words = wallet->getSeed(); + QString seed_25_words = wallet->getSeed(seedOffset); if (seed_14_words.isEmpty()) { ui->check_toggleSeedType->hide(); @@ -24,6 +25,9 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent) ui->frameRestoreHeight->setVisible(false); } + ui->frameSeedOffset->setVisible(!seedOffset.isEmpty()); + ui->line_seedOffset->setText(seedOffset); + connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed_14_words](bool toggled){ this->setSeed(toggled ? seed_25_words : seed_14_words); ui->frameRestoreHeight->setVisible(toggled); diff --git a/src/dialog/seeddialog.ui b/src/dialog/seeddialog.ui index 1c50416..020588c 100644 --- a/src/dialog/seeddialog.ui +++ b/src/dialog/seeddialog.ui @@ -7,7 +7,7 @@ 0 0 519 - 330 + 543 @@ -63,6 +63,32 @@ + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Seed offset: + + + + + + + true + + + + + + diff --git a/src/globals.h b/src/globals.h index 3755e54..cb9d031 100644 --- a/src/globals.h +++ b/src/globals.h @@ -5,10 +5,23 @@ #define FEATHER_GLOBALS_H #include +#include namespace globals { + // coin constants + const std::string coinName = "monero"; const qreal cdiv = 1e12; + const quint32 mixin = 10; + const quint64 kdfRounds = 1; + + // donation constants + const QString donationAddress = "47ntfT2Z5384zku39pTM6hGcnLnvpRYW2Azm87GiAAH2bcTidtq278TL6HmwyL8yjMeERqGEBs3cqC8vvHPJd1cWQrGC65f"; + const int donationAmount = 25; // euro + const int donationBoundary = 15; + + // websocket constants + const QUrl websocketUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws")); } #endif //FEATHER_GLOBALS_H diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 3cbefcb..b47b34b 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -35,9 +35,9 @@ Wallet::ConnectionStatus Wallet::connectionStatus() const return m_connectionStatus; } -QString Wallet::getSeed() const +QString Wallet::getSeed(const QString &seedOffset) const { - return QString::fromStdString(m_walletImpl->seed()); + return QString::fromStdString(m_walletImpl->seed(seedOffset.toStdString())); } QString Wallet::getSeedLanguage() const diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index d48f0b4..56f2013 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -76,7 +76,6 @@ class Wallet : public QObject, public PassprasePrompter Q_OBJECT Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged) Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged) - Q_PROPERTY(QString seed READ getSeed) Q_PROPERTY(QString seedLanguage READ getSeedLanguage) Q_PROPERTY(Status status READ status) Q_PROPERTY(NetworkType::Type nettype READ nettype) @@ -127,7 +126,7 @@ public: ConnectionStatus connectionStatus() const; //! returns mnemonic seed - QString getSeed() const; + QString getSeed(const QString &seedOffset) const; //! returns seed language QString getSeedLanguage() const; diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index e5427f0..a6d9536 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -142,7 +142,7 @@ Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString & } Wallet *WalletManager::createDeterministicWalletFromSpendKey(const QString &path, const QString &password, const QString &language, NetworkType::Type nettype, - const QString &spendkey, quint64 restoreHeight, quint64 kdfRounds) + const QString &spendkey, quint64 restoreHeight, quint64 kdfRounds, const QString &offset_passphrase) { QMutexLocker locker(&m_mutex); if (m_currentWallet) { @@ -151,7 +151,7 @@ Wallet *WalletManager::createDeterministicWalletFromSpendKey(const QString &path m_currentWallet = NULL; } Monero::Wallet * w = m_pimpl->createDeterministicWalletFromSpendKey(path.toStdString(), "", language.toStdString(), static_cast(nettype), restoreHeight, - spendkey.toStdString(), kdfRounds); + spendkey.toStdString(), kdfRounds, offset_passphrase.toStdString()); m_currentWallet = new Wallet(w); return m_currentWallet; } diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index f7d07fd..6f5efab 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -76,7 +76,8 @@ public: NetworkType::Type nettype, const QString &spendkey, quint64 restoreHeight, - quint64 kdfRounds); + quint64 kdfRounds, + const QString &offset_passphrase = ""); Q_INVOKABLE Wallet * createWalletFromDevice(const QString &path, const QString &password, diff --git a/src/main.cpp b/src/main.cpp index 2d0ace4..c7fd814 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,9 +51,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) { QCommandLineOption torPortOption(QStringList() << "tor-port", "Port of running Tor instance.", "torPort"); parser.addOption(torPortOption); - QCommandLineOption debugModeOption(QStringList() << "debug", "Run program in debug mode."); - parser.addOption(debugModeOption); - QCommandLineOption quietModeOption(QStringList() << "quiet", "Limit console output"); parser.addOption(quietModeOption); @@ -94,7 +91,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) { } const QStringList args = parser.positionalArguments(); - bool debugMode = parser.isSet(debugModeOption); bool localTor = parser.isSet(useLocalTorOption); bool stagenet = parser.isSet(stagenetOption); bool testnet = parser.isSet(testnetOption); @@ -111,8 +107,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) { QCoreApplication::setOrganizationName("featherwallet.org"); auto *ctx = new AppContext(&parser); - ctx->applicationPath = QString(argv[0]); - ctx->isDebug = debugMode; auto *cli = new CLI(ctx, &cli_app); QObject::connect(cli, &CLI::closeApplication, &cli_app, &QCoreApplication::quit); @@ -162,8 +156,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) { } auto *ctx = new AppContext(&parser); - ctx->applicationPath = QString(argv[0]); - ctx->isDebug = debugMode; #if defined(Q_OS_MAC) // For some odd reason, if we don't do this, QPushButton's diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0b4eee4..3b2b31c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -503,6 +503,7 @@ void MainWindow::initWidgets() { WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){ auto *wizard = new WalletWizard(m_ctx, startPage, this); + connect(wizard, &WalletWizard::skinChanged, this, &MainWindow::skinChanged); connect(wizard, &WalletWizard::openWallet, m_ctx, &AppContext::onOpenWallet); connect(wizard, &WalletWizard::defaultWalletDirChanged, m_windowSettings, &Settings::updatePaths); connect(wizard, &WalletWizard::rejected, [this]{ @@ -544,7 +545,7 @@ void MainWindow::touchbarShowWallet() { void MainWindow::onWalletCreatedError(const QString &err) { QMessageBox::warning(this, "Wallet creation error", err); - this->showWizard(WalletWizard::Page_CreateWallet); + this->showWizard(WalletWizard::Page_WalletFile); } void MainWindow::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) { @@ -1015,11 +1016,11 @@ void MainWindow::closeEvent(QCloseEvent *event) { } void MainWindow::donateButtonClicked() { - double donation = AppContext::prices->convert("EUR", "XMR", m_ctx->donationAmount); + double donation = AppContext::prices->convert("EUR", "XMR", globals::donationAmount); if (donation <= 0) donation = 0.1337; - ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the Feather development team", donation); + ui->sendWidget->fill(globals::donationAddress, "Donation to the Feather development team", donation); ui->tabWidget->setCurrentIndex(Tabs::SEND); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 0fa054a..09b77a7 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -28,7 +28,7 @@ #include "utils/networking.h" #include "appcontext.h" #include "utils/config.h" -#include "wizard/walletwizard.h" +#include "wizard/WalletWizard.h" #include "settings.h" #include "dialog/aboutdialog.h" #include "dialog/signverifydialog.h" diff --git a/src/settings.cpp b/src/settings.cpp index 8029288..4ab33a1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -71,7 +71,6 @@ Settings::Settings(QWidget *parent) : QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly); if (walletDir.isEmpty()) return; m_ctx->defaultWalletDir = walletDir; - m_ctx->defaultWalletDirRoot = walletDir; config()->set(Config::walletDirectory, walletDir); ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir); }); @@ -81,8 +80,8 @@ Settings::Settings(QWidget *parent) : void Settings::updatePaths() { ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir); - ui->lineEdit_configDir->setText(m_ctx->configDirectory); - ui->lineEdit_applicationDir->setText(m_ctx->applicationPath); + ui->lineEdit_configDir->setText(Config::defaultConfigDir().path()); + ui->lineEdit_applicationDir->setText(QCoreApplication::applicationDirPath()); } void Settings::fiatCurrencySelected(int index) { diff --git a/src/utils/RestoreHeightLookup.h b/src/utils/RestoreHeightLookup.h index 3181df4..8ae4292 100644 --- a/src/utils/RestoreHeightLookup.h +++ b/src/utils/RestoreHeightLookup.h @@ -23,7 +23,10 @@ struct RestoreHeightLookup { // will calculate the blockheight based off the last known // date: ((now - lastKnownDate) / blockTime) - clearance - if(this->type == NetworkType::TESTNET) return 1; + if (this->type == NetworkType::TESTNET) { + return 1; + } + int blockTime = 120; int blocksPerDay = 86400 / blockTime; int blockCalcClearance = blocksPerDay * 5; @@ -50,12 +53,20 @@ struct RestoreHeightLookup { // @TODO: most likely inefficient, refactor QMap::iterator i; int timestamp = 0; + int heightData = 1; for (i = this->data.begin(); i != this->data.end(); ++i) { int ts = i.key(); if (i.value() > height) return timestamp; timestamp = ts; + heightData = i.value(); } + + while (heightData < height) { + heightData += 720; // blocks per day + timestamp += 86400; // seconds in day + } + return timestamp; } @@ -67,7 +78,7 @@ struct RestoreHeightLookup { for(const auto &line: data.split('\n')) { if(line.trimmed().isEmpty()) continue; auto spl = line.trimmed().split(':'); - rtn->data[spl.at(0).toUInt()] = spl.at(1).toUInt(); + rtn->data[spl.at(0).toInt()] = spl.at(1).toInt(); } return rtn; } diff --git a/src/utils/config.cpp b/src/utils/config.cpp index 61fd4a0..10efcdd 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -104,31 +104,47 @@ Config::Config(const QString& fileName, QObject* parent) } Config::Config(QObject* parent) - : QObject(parent) + : QObject(parent) { - QString configPath; + QDir configDir = Config::defaultConfigDir(); + + QString portablePath = QCoreApplication::applicationDirPath().append("/%1"); + if (QFile::exists(portablePath.arg(".portable"))) { + init(portablePath.arg("feather_data/settings.json")); + return; + } bool isTails = TailsOS::detect(); - - configPath = QDir::homePath(); if (isTails) { // #if defined(PORTABLE) QString appImagePath = qgetenv("APPIMAGE"); QFileInfo appImageDir(appImagePath); - QDir portablePath(appImageDir.absoluteDir().path() + "/.feather"); + QDir portablePath(appImageDir.absoluteDir().path() + "/feather_data"); if (portablePath.mkpath(".")) { - configPath = portablePath.path(); - } - else { + configDir = portablePath; + } else { qCritical() << "Unable to create portable directory: " << portablePath.path(); } } - configPath += "/.config/feather/settings.json"; + QString configPath = configDir.filePath("settings.json"); init(QDir::toNativeSeparators(configPath)); } +QDir Config::defaultConfigDir() { +#if defined(Q_OS_WIN) + return QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); +#elif defined(Q_OS_MACOS) + return QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); +#else + return QDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/feather"); +#endif +} + +QDir Config::defaultPortableConfigDir() { + return QDir(QCoreApplication::applicationDirPath() + "/feather_data"); +} Config::~Config() { diff --git a/src/utils/config.h b/src/utils/config.h index 9271789..7effce1 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -9,6 +9,7 @@ #include #include #include +#include class Config : public QObject { @@ -50,7 +51,8 @@ public: redditFrontend, showHistorySyncNotice, GUI_HistoryViewState, - amountPrecision + amountPrecision, + portableMode }; ~Config() override; @@ -60,6 +62,9 @@ public: void sync(); void resetToDefaults(); + static QDir defaultConfigDir(); + static QDir defaultPortableConfigDir(); + static Config* instance(); signals: diff --git a/src/utils/keysfiles.cpp b/src/utils/keysfiles.cpp index cc5bbfe..addd896 100644 --- a/src/utils/keysfiles.cpp +++ b/src/utils/keysfiles.cpp @@ -69,14 +69,14 @@ void WalletKeysFilesModel::refresh() { void WalletKeysFilesModel::updateDirectories() { this->walletDirectories.clear(); - this->walletDirectories << m_ctx->defaultWalletDirRoot; + this->walletDirectories << m_ctx->defaultWalletDir; // TODO auto walletPath = config()->get(Config::walletPath).toString(); if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) { QDir d = QFileInfo(walletPath).absoluteDir(); this->walletDirectories << d.absolutePath(); } - this->walletDirectories << m_ctx->homeDir; + this->walletDirectories << QDir::homePath(); this->walletDirectories.removeDuplicates(); } @@ -160,12 +160,13 @@ QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const { return QString("main"); } case ModelColumns::FileName: - return walletKeyFile.fileName(); + return walletKeyFile.fileName().replace(".keys", ""); case ModelColumns::Path: { auto fp = walletKeyFile.path(); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) - if (fp.startsWith(m_ctx->homeDir)) - fp = QString("~/%1").arg(fp.remove(0, m_ctx->homeDir.length() + 1)); + if (fp.startsWith(QDir::homePath())) { + fp = QString("~/%1").arg(fp.remove(0, QDir::homePath().length() + 1)); + } #endif return fp; } diff --git a/src/utils/tor.cpp b/src/utils/tor.cpp index d1ab811..1f6d186 100644 --- a/src/utils/tor.cpp +++ b/src/utils/tor.cpp @@ -20,7 +20,7 @@ Tor::Tor(AppContext *ctx, QObject *parent) { connect(m_checkConnectionTimer, &QTimer::timeout, this, &Tor::checkConnection); - this->torDir = QDir(m_ctx->configDirectory).filePath("tor"); + this->torDir = Config::defaultConfigDir().filePath("tor"); this->torDataPath = QDir(this->torDir).filePath("data"); if (m_ctx->cmdargs->isSet("tor-port")) { diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 6be4201..1da6683 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -468,4 +468,22 @@ QTextCharFormat Utils::addressTextFormat(const SubaddressIndex &index) { return rec; } return QTextCharFormat(); +} + +bool Utils::isTorsocks() { +#if defined(Q_OS_MAC) + return qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0; +#elif defined(Q_OS_LINUX) + return qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0; +#else + return false; +#endif +} + +QString Utils::defaultWalletDir() { +#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) + return QString("%1/Monero/wallets").arg(QDir::homePath()); +#elif defined(Q_OS_WIN) + return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero/wallets"; +#endif } \ No newline at end of file diff --git a/src/utils/utils.h b/src/utils/utils.h index b0b4294..05ca74a 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -76,6 +76,8 @@ public: static QMap localeCache; static QString balanceFormat(quint64 balance); static QTextCharFormat addressTextFormat(const SubaddressIndex &index); + static bool isTorsocks(); + static QString defaultWalletDir(); template static QString QtEnumToString (const QEnum value) diff --git a/src/wizard/PageMenu.cpp b/src/wizard/PageMenu.cpp new file mode 100644 index 0000000..c0f97f9 --- /dev/null +++ b/src/wizard/PageMenu.cpp @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "WalletWizard.h" +#include "PageMenu.h" +#include "ui_PageMenu.h" + +#include + +PageMenu::PageMenu(AppContext *ctx, WizardFields *fields, WalletKeysFilesModel *wallets, QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::PageMenu) + , m_ctx(ctx) + , m_walletKeysFilesModel(wallets) + , m_fields(fields) +{ + ui->setupUi(this); + this->setButtonText(QWizard::FinishButton, "Open recent wallet"); + +#if defined(Q_OS_MAC) + ui->check_darkMode->setVisible(false); +#endif + + QString settingsSkin = config()->get(Config::skin).toString(); + ui->check_darkMode->setChecked(settingsSkin == "QDarkStyle"); + + connect(ui->check_darkMode, &QCheckBox::toggled, this, &PageMenu::enableDarkMode); +} + +void PageMenu::initializePage() { + if (m_walletKeysFilesModel->rowCount() > 0) { + ui->radioOpen->setChecked(true); + } else { + ui->radioCreate->setChecked(true); + } +} + +int PageMenu::nextId() const { + if (ui->radioCreate->isChecked()) + return WalletWizard::Page_CreateWalletSeed; + if (ui->radioOpen->isChecked()) + return WalletWizard::Page_OpenWallet; + if (ui->radioSeed->isChecked()) + return WalletWizard::Page_WalletRestoreSeed; + if (ui->radioViewOnly->isChecked()) + return WalletWizard::Page_WalletRestoreKeys; + return 0; +} + +bool PageMenu::validatePage() { + if (ui->radioCreate->isChecked()) { + m_fields->mode = WizardMode::CreateWallet; + m_fields->modeText = "Create wallet"; + } + if (ui->radioOpen->isChecked()) { + m_fields->mode = WizardMode::OpenWallet; + m_fields->modeText = "Open wallet"; + } + if (ui->radioSeed->isChecked()) { + m_fields->mode = WizardMode::RestoreFromSeed; + m_fields->modeText = "Restore wallet"; + } + if (ui->radioViewOnly->isChecked()) { + m_fields->mode = WizardMode::RestoreFromKeys; + m_fields->modeText = "Restore wallet"; + } + + return true; +} \ No newline at end of file diff --git a/src/wizard/menu.h b/src/wizard/PageMenu.h similarity index 64% rename from src/wizard/menu.h rename to src/wizard/PageMenu.h index d62c999..c755a98 100644 --- a/src/wizard/menu.h +++ b/src/wizard/PageMenu.h @@ -11,23 +11,27 @@ #include "appcontext.h" namespace Ui { - class MenuPage; + class PageMenu; } -class MenuPage : public QWizardPage +class PageMenu : public QWizardPage { Q_OBJECT public: - explicit MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr); + explicit PageMenu(AppContext *ctx, WizardFields *fields, WalletKeysFilesModel *wallets, QWidget *parent = nullptr); void initializePage() override; bool validatePage() override; int nextId() const override; +signals: + void enableDarkMode(bool enable); + private: AppContext *m_ctx; WalletKeysFilesModel *m_walletKeysFilesModel; - Ui::MenuPage *ui; + Ui::PageMenu *ui; + WizardFields *m_fields; }; #endif //FEATHER_WIZARDMENU_H diff --git a/src/wizard/menu.ui b/src/wizard/PageMenu.ui similarity index 68% rename from src/wizard/menu.ui rename to src/wizard/PageMenu.ui index a45d1ee..92e8117 100644 --- a/src/wizard/menu.ui +++ b/src/wizard/PageMenu.ui @@ -1,7 +1,7 @@ - MenuPage - + PageMenu + 0 @@ -63,7 +63,7 @@ Qt::ClickFocus - Import from keys + Restore wallet from keys @@ -81,24 +81,39 @@ - - - false - + - by dsc & tobtoht + Dark mode - - - false - - - banner: themonera.art - - + + + + + + + false + + + by dsc & tobtoht + + + + + + + false + + + banner: themonera.art + + + + + + diff --git a/src/wizard/network.cpp b/src/wizard/PageNetwork.cpp similarity index 83% rename from src/wizard/network.cpp rename to src/wizard/PageNetwork.cpp index 53ffa0d..ce433c0 100644 --- a/src/wizard/network.cpp +++ b/src/wizard/PageNetwork.cpp @@ -1,23 +1,21 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "wizard/network.h" -#include "ui_network.h" +#include "PageNetwork.h" +#include "ui_PageNetwork.h" #include -NetworkPage::NetworkPage(AppContext *ctx, QWidget *parent) : +// Unused for now +PageNetwork::PageNetwork(AppContext *ctx, QWidget *parent) : QWizardPage(parent), - ui(new Ui::NetworkPage), + ui(new Ui::PageNetwork), m_ctx(ctx) { ui->setupUi(this); this->setTitle("Welcome to Feather!"); ui->customFrame->hide(); - QPixmap p(":assets/images/feather.png"); - ui->featherImage->setText(""); - ui->featherImage->setPixmap(p.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); ui->label_eg->setText("Examples:\n- http://127.0.0.1:18089\n- my.node.com\n- my.node.com:18089\n- user:pass@my.node.com:18089"); auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt(); @@ -44,11 +42,11 @@ NetworkPage::NetworkPage(AppContext *ctx, QWidget *parent) : }); } -int NetworkPage::nextId() const { +int PageNetwork::nextId() const { return 0; } -bool NetworkPage::validatePage() { +bool PageNetwork::validatePage() { auto cfg = config()->get(Config::nodeSource); if(ui->radioRemote->isChecked()) { if(cfg != NodeSource::websocket) diff --git a/src/wizard/network.h b/src/wizard/PageNetwork.h similarity index 75% rename from src/wizard/network.h rename to src/wizard/PageNetwork.h index 61b276c..a180dd5 100644 --- a/src/wizard/network.h +++ b/src/wizard/PageNetwork.h @@ -12,22 +12,22 @@ #include "utils/nodes.h" namespace Ui { - class NetworkPage; + class PageNetwork; } -class NetworkPage : public QWizardPage +class PageNetwork : public QWizardPage { Q_OBJECT public: - explicit NetworkPage(AppContext *ctx, QWidget *parent = nullptr); + explicit PageNetwork(AppContext *ctx, QWidget *parent = nullptr); bool validatePage() override; int nextId() const override; private: AppContext *m_ctx; QLabel *topLabel; - Ui::NetworkPage *ui; + Ui::PageNetwork *ui; }; #endif //FEATHER_WIZARDNETWORK_H diff --git a/src/wizard/network.ui b/src/wizard/PageNetwork.ui similarity index 98% rename from src/wizard/network.ui rename to src/wizard/PageNetwork.ui index 62a5460..481e0f0 100644 --- a/src/wizard/network.ui +++ b/src/wizard/PageNetwork.ui @@ -1,7 +1,7 @@ - NetworkPage - + PageNetwork + 0 diff --git a/src/wizard/openwallet.cpp b/src/wizard/PageOpenWallet.cpp similarity index 83% rename from src/wizard/openwallet.cpp rename to src/wizard/PageOpenWallet.cpp index f643495..cb76f27 100644 --- a/src/wizard/openwallet.cpp +++ b/src/wizard/PageOpenWallet.cpp @@ -1,15 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "wizard/openwallet.h" -#include "ui_openwallet.h" +#include "PageOpenWallet.h" +#include "ui_PageOpenWallet.h" #include #include -OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent) +PageOpenWallet::PageOpenWallet(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent) : QWizardPage(parent) - , ui(new Ui::OpenWalletPage) + , ui(new Ui::PageOpenWallet) , m_ctx(ctx) , m_walletKeysFilesModel(wallets) { @@ -18,7 +18,7 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, Q connect(ui->btnBrowse, &QPushButton::clicked, [=]{ // manually browsing for wallet auto walletPath = config()->get(Config::walletPath).toString(); - if(walletPath.isEmpty()) + if (walletPath.isEmpty()) walletPath = m_ctx->defaultWalletDir; QString path = QFileDialog::getOpenFileName(this, "Select your wallet file", walletPath, "Wallet file (*.keys)"); if(path.isEmpty()) return; @@ -29,9 +29,7 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, Q return; } - setField("walletPath", path); - - if(ui->openOnStartup->isChecked()) + if (ui->openOnStartup->isChecked()) config()->set(Config::autoOpenWalletPath, QString("%1%2").arg(m_ctx->networkType).arg(path)); emit openWallet(path); @@ -59,29 +57,29 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, Q connect(ui->walletTable->selectionModel(), &QItemSelectionModel::currentRowChanged, [this](QModelIndex current, QModelIndex prev){ this->updatePath(); }); - connect(ui->walletTable, &QTreeView::doubleClicked, this, &OpenWalletPage::validatePage); + connect(ui->walletTable, &QTreeView::doubleClicked, this, &PageOpenWallet::validatePage); } -void OpenWalletPage::initializePage() { +void PageOpenWallet::initializePage() { m_walletKeysFilesModel->refresh(); } -void OpenWalletPage::updatePath() { +void PageOpenWallet::updatePath() { QModelIndex index = ui->walletTable->currentIndex(); if (!index.isValid()) { - ui->labelPath->clear(); + ui->linePath->clear(); return; } QString path = index.model()->data(index.siblingAtColumn(WalletKeysFilesModel::Path), Qt::DisplayRole).toString(); - ui->labelPath->setText(path); + ui->linePath->setText(path); } -int OpenWalletPage::nextId() const { +int PageOpenWallet::nextId() const { return -1; } -bool OpenWalletPage::validatePage() { +bool PageOpenWallet::validatePage() { QModelIndex index = ui->walletTable->currentIndex(); if(!index.isValid()) { QMessageBox::warning(this, "Wallet not selected", "Please select a wallet from the list."); diff --git a/src/wizard/openwallet.h b/src/wizard/PageOpenWallet.h similarity index 81% rename from src/wizard/openwallet.h rename to src/wizard/PageOpenWallet.h index d3efa7e..73309d6 100644 --- a/src/wizard/openwallet.h +++ b/src/wizard/PageOpenWallet.h @@ -12,15 +12,15 @@ #include "utils/keysfiles.h" namespace Ui { - class OpenWalletPage; + class PageOpenWallet; } -class OpenWalletPage : public QWizardPage +class PageOpenWallet : public QWizardPage { Q_OBJECT public: - explicit OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr); + explicit PageOpenWallet(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr); void initializePage() override; bool validatePage() override; int nextId() const override; @@ -34,7 +34,7 @@ private: AppContext *m_ctx; WalletKeysFilesModel *m_walletKeysFilesModel; WalletKeysFilesProxyModel *m_keysProxy; - Ui::OpenWalletPage *ui; + Ui::PageOpenWallet *ui; QStandardItemModel *m_model; }; diff --git a/src/wizard/openwallet.ui b/src/wizard/PageOpenWallet.ui similarity index 71% rename from src/wizard/openwallet.ui rename to src/wizard/PageOpenWallet.ui index f2c3162..5284e32 100644 --- a/src/wizard/openwallet.ui +++ b/src/wizard/PageOpenWallet.ui @@ -1,19 +1,37 @@ - OpenWalletPage - + PageOpenWallet + 0 0 - 729 - 414 + 706 + 440 WizardPage + + + + + + true + + + + + + + Browse + + + + + @@ -24,7 +42,7 @@ - + @@ -46,24 +64,16 @@ - - - - Browse - - - - - - - - - - + + walletTable + linePath + btnBrowse + openOnStartup + diff --git a/src/wizard/PageSetPassword.cpp b/src/wizard/PageSetPassword.cpp new file mode 100644 index 0000000..f4b6c93 --- /dev/null +++ b/src/wizard/PageSetPassword.cpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "PageSetPassword.h" +#include "ui_PageSetPassword.h" +#include "WalletWizard.h" + +PageSetPassword::PageSetPassword(AppContext *ctx, WizardFields *fields, QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::PageSetPassword) + , m_ctx(ctx) + , m_fields(fields) +{ + ui->setupUi(this); + this->setFinalPage(true); + + QPixmap pixmap = QPixmap(":/assets/images/lock.png"); + ui->icon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation)); + + connect(ui->line_password, &QLineEdit::textChanged, [this]{ + this->completeChanged(); + }); + connect(ui->line_confirmPassword, &QLineEdit::textChanged, [this]{ + this->completeChanged(); + }); + + this->setButtonText(QWizard::FinishButton, "Create/Open wallet"); +} + +void PageSetPassword::initializePage() { + this->setTitle(m_fields->modeText); + ui->line_password->setText(""); + ui->line_password->setText(""); +} + +bool PageSetPassword::validatePage() { + m_fields->password = ui->line_password->text(); + emit createWallet(); + return true; +} + +int PageSetPassword::nextId() const { + return -1; +} + +bool PageSetPassword::isComplete() const { + return ui->line_password->text() == ui->line_confirmPassword->text(); +} diff --git a/src/wizard/PageSetPassword.h b/src/wizard/PageSetPassword.h new file mode 100644 index 0000000..aa89c5c --- /dev/null +++ b/src/wizard/PageSetPassword.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#ifndef FEATHER_PASSWORD_H +#define FEATHER_PASSWORD_H + +#include +#include + +#include "appcontext.h" +#include "WalletWizard.h" + +namespace Ui { + class PageSetPassword; +} + +class PageSetPassword : public QWizardPage +{ +Q_OBJECT + +public: + explicit PageSetPassword(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr); + void initializePage() override; + bool validatePage() override; + int nextId() const override; + bool isComplete() const override; + +signals: + void createWallet(); + +private: + AppContext *m_ctx; + WizardFields *m_fields; + Ui::PageSetPassword *ui; +}; + + +#endif //FEATHER_PASSWORD_H diff --git a/src/wizard/PageSetPassword.ui b/src/wizard/PageSetPassword.ui new file mode 100644 index 0000000..66da66a --- /dev/null +++ b/src/wizard/PageSetPassword.ui @@ -0,0 +1,116 @@ + + + PageSetPassword + + + + 0 + 0 + 431 + 231 + + + + WizardPage + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + TextLabel + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Choose a password to encrypt your wallet keys. + + + true + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + Confirm Password: + + + + + + + QLineEdit::Password + + + + + + + + diff --git a/src/wizard/PageSetRestoreHeight.cpp b/src/wizard/PageSetRestoreHeight.cpp new file mode 100644 index 0000000..046feb9 --- /dev/null +++ b/src/wizard/PageSetRestoreHeight.cpp @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include +#include "PageSetRestoreHeight.h" +#include "ui_PageSetRestoreHeight.h" +#include "WalletWizard.h" + +PageSetRestoreHeight::PageSetRestoreHeight(AppContext *ctx, WizardFields *fields, QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::PageSetRestoreHeight) + , m_ctx(ctx) + , m_fields(fields) +{ + ui->setupUi(this); + + QRegExp yearRe(R"(\d{2,4}-\d{1,2}-\d{1,2})"); + QValidator *yearValidator = new QRegExpValidator(yearRe, this); + ui->line_creationDate->setValidator(yearValidator); + + QRegExp heightRe(R"(\d{7})"); + QValidator *heightValidator = new QRegExpValidator(heightRe, this); + ui->line_restoreHeight->setValidator(heightValidator); + + QPixmap pixmap = QPixmap(":/assets/images/unpaid.png"); + ui->icon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation)); + + QPixmap pixmap2 = QPixmap(":/assets/images/info.png"); + ui->warningIcon->setPixmap(pixmap2.scaledToWidth(32, Qt::SmoothTransformation)); + ui->infoIcon->setPixmap(pixmap2.scaledToWidth(32, Qt::SmoothTransformation)); + + ui->frame_scanWarning->hide(); + ui->frame_walletAgeWarning->hide(); + + connect(ui->line_creationDate, &QLineEdit::textEdited, [this]{ + this->onCreationDateEdited(); + this->completeChanged(); + }); + connect(ui->line_restoreHeight, &QLineEdit::textEdited, [this]{ + this->onRestoreHeightEdited(); + this->completeChanged(); + }); +} + +void PageSetRestoreHeight::initializePage() { + this->setTitle("Restore height"); + ui->line_creationDate->setText(""); + ui->line_restoreHeight->setText(""); +} + +void PageSetRestoreHeight::onCreationDateEdited() { + auto curDate = QDateTime::currentDateTime().addDays(-7); + auto date = QDateTime::fromString(ui->line_creationDate->text(), "yyyy-MM-dd"); + if (!date.isValid()) { + ui->frame_walletAgeWarning->hide(); + ui->frame_scanWarning->hide(); + ui->line_restoreHeight->setText(""); + return; + } + + QDateTime restoreDate = date > curDate ? curDate : date; + int timestamp = restoreDate.toSecsSinceEpoch(); + + QString restoreHeight = QString::number(m_ctx->restoreHeights[m_ctx->networkType]->dateToRestoreHeight(timestamp)); + ui->line_restoreHeight->setText(restoreHeight); + + this->showScanWarning(restoreDate); + this->showWalletAgeWarning(restoreDate); +} + +void PageSetRestoreHeight::onRestoreHeightEdited() { + int restoreHeight = ui->line_restoreHeight->text().toInt(); + if (restoreHeight == 0) { + ui->frame_walletAgeWarning->hide(); + ui->frame_scanWarning->hide(); + ui->line_creationDate->setText(""); + return; + } + + int timestamp = m_ctx->restoreHeights[m_ctx->networkType]->restoreHeightToDate(restoreHeight); + auto date = QDateTime::fromSecsSinceEpoch(timestamp); + ui->line_creationDate->setText(date.toString("yyyy-MM-dd")); + + this->showScanWarning(date); + this->showWalletAgeWarning(date); +} + +void PageSetRestoreHeight::showScanWarning(const QDateTime &date) { + QString dateString = date.toString("yyyy-MM-dd"); + ui->label_scanWarning->setText(QString("Wallet will not scan for transactions before %1").arg(dateString)); + ui->frame_scanWarning->show(); +} + +void PageSetRestoreHeight::showWalletAgeWarning(const QDateTime &date) { + QDateTime yearAgo = QDateTime::currentDateTime().addYears(-1); + ui->frame_walletAgeWarning->setVisible(date < yearAgo); +} + +bool PageSetRestoreHeight::validatePage() { + m_fields->restoreHeight = ui->line_restoreHeight->text().toInt(); + return true; +} + +int PageSetRestoreHeight::nextId() const { + return WalletWizard::Page_WalletFile; +} + +bool PageSetRestoreHeight::isComplete() const { + return !ui->line_restoreHeight->text().isEmpty(); +} diff --git a/src/wizard/PageSetRestoreHeight.h b/src/wizard/PageSetRestoreHeight.h new file mode 100644 index 0000000..155406b --- /dev/null +++ b/src/wizard/PageSetRestoreHeight.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#ifndef FEATHER_PAGESETRESTOREHEIGHT_H +#define FEATHER_PAGESETRESTOREHEIGHT_H + +#include +#include + +#include "appcontext.h" +#include "WalletWizard.h" + +namespace Ui { + class PageSetRestoreHeight; +} + +class PageSetRestoreHeight : public QWizardPage +{ +Q_OBJECT + +public: + explicit PageSetRestoreHeight(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr); + void initializePage() override; + bool validatePage() override; + int nextId() const override; + bool isComplete() const override; + +private slots: + void onCreationDateEdited(); + void onRestoreHeightEdited(); + +private: + void showScanWarning(const QDateTime &date); + void showWalletAgeWarning(const QDateTime &date); + + AppContext *m_ctx; + WizardFields *m_fields; + Ui::PageSetRestoreHeight *ui; +}; + +#endif //FEATHER_PAGESETRESTOREHEIGHT_H diff --git a/src/wizard/PageSetRestoreHeight.ui b/src/wizard/PageSetRestoreHeight.ui new file mode 100644 index 0000000..52be444 --- /dev/null +++ b/src/wizard/PageSetRestoreHeight.ui @@ -0,0 +1,279 @@ + + + PageSetRestoreHeight + + + + 0 + 0 + 676 + 447 + + + + WizardPage + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + + Enter the wallet creation date or set the restore height manually. + + + true + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + + Wallet creation date: + + + + + + + + + YYYY-MM-DD + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Restore height: + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Specifying a restore height that is too high may result in an inaccurate balance. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Wallet is very old. Synchronization may take a long time. + + + true + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Wallet will not scan for transactions before YYYY/MM/DD. + + + true + + + + + + + + + + + diff --git a/src/wizard/PageWalletFile.cpp b/src/wizard/PageWalletFile.cpp new file mode 100644 index 0000000..02d4169 --- /dev/null +++ b/src/wizard/PageWalletFile.cpp @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "WalletWizard.h" +#include "PageWalletFile.h" +#include "ui_PageWalletFile.h" + +#include "utils/utils.h" + +#include +#include + +PageWalletFile::PageWalletFile(AppContext *ctx, WizardFields *fields, QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::PageWalletFile) + , m_ctx(ctx) + , m_fields(fields) +{ + ui->setupUi(this); + this->setButtonText(QWizard::FinishButton, "Open wallet"); + + QPixmap pixmap = QPixmap(":/assets/images/file.png"); + ui->lockIcon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation)); + + this->registerField("walletName", ui->line_walletName); + this->registerField("walletDirectory", ui->line_walletDir); + + connect(ui->btnChange, &QPushButton::clicked, [=] { + QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly); + if(walletDir.isEmpty()) return; + m_ctx->defaultWalletDir = walletDir; + ui->line_walletDir->setText(walletDir); + config()->set(Config::walletDirectory, walletDir); + emit defaultWalletDirChanged(walletDir); + }); + + connect(ui->line_walletName, &QLineEdit::textChanged, this, &PageWalletFile::validateWidgets); + connect(ui->line_walletDir, &QLineEdit::textChanged, this, &PageWalletFile::validateWidgets); +} + +void PageWalletFile::initializePage() { + this->setTitle(m_fields->modeText); + ui->line_walletDir->setText(m_ctx->defaultWalletDir); + ui->line_walletName->setText(this->defaultWalletName()); +} + +bool PageWalletFile::validateWidgets(){ + QString walletName = ui->line_walletName->text(); + QString walletDir = ui->line_walletDir->text(); + + m_validated = true; + ui->line_walletName->setStyleSheet(""); + ui->line_walletDir->setStyleSheet(""); + QString errStyle = "QLineEdit{border: 1px solid red;}"; + + if (walletDir.isEmpty()) { + ui->line_walletDir->setStyleSheet(errStyle); + m_validated = false; + } + + if (!Utils::dirExists(walletDir)) { + ui->line_walletDir->setStyleSheet(errStyle); + m_validated = false; + } + + if (walletName.isEmpty()) { + ui->line_walletName->setStyleSheet(errStyle); + m_validated = false; + } + + if (this->walletPathExists(walletName)) { + ui->line_walletName->setStyleSheet(errStyle); + m_validated = false; + } + + this->completeChanged(); + return m_validated; +} + +int PageWalletFile::nextId() const { + return WalletWizard::Page_SetPasswordPage; +} + +bool PageWalletFile::validatePage() { + if (!this->validateWidgets()) { + return false; + } + + m_fields->walletName = ui->line_walletName->text(); + m_fields->walletDir = ui->line_walletDir->text(); + + return true; +} + +bool PageWalletFile::isComplete() const { + return m_validated; +} + +QString PageWalletFile::defaultWalletName() { + int count = 1; + QString walletName; + do { + walletName = QString("wallet_%1").arg(count); + count++; + } while (this->walletPathExists(walletName)); + + return walletName; +} + +bool PageWalletFile::walletPathExists(const QString &walletName) { + QDir walletDir = QDir(ui->line_walletDir->text()); + + return QFile::exists(walletDir.filePath(walletName)) || + QFile::exists(walletDir.filePath(QString("%1.keys").arg(walletName))); +} \ No newline at end of file diff --git a/src/wizard/createwallet.h b/src/wizard/PageWalletFile.h similarity index 58% rename from src/wizard/createwallet.h rename to src/wizard/PageWalletFile.h index 28a4675..3d2b495 100644 --- a/src/wizard/createwallet.h +++ b/src/wizard/PageWalletFile.h @@ -7,32 +7,37 @@ #include #include #include +#include #include "appcontext.h" namespace Ui { - class CreateWalletPage; + class PageWalletFile; } -class CreateWalletPage : public QWizardPage +class PageWalletFile : public QWizardPage { Q_OBJECT public: - explicit CreateWalletPage(AppContext *ctx, QWidget *parent = nullptr); + explicit PageWalletFile(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr); void initializePage() override; bool validatePage() override; int nextId() const override; + bool isComplete() const override; signals: - void createWallet(); void defaultWalletDirChanged(QString walletDir); private: - AppContext *m_ctx; - Ui::CreateWalletPage *ui; - QString m_walletDir; + QString defaultWalletName(); + bool walletPathExists(const QString &walletName); bool validateWidgets(); + + AppContext *m_ctx; + Ui::PageWalletFile *ui; + WizardFields *m_fields; + bool m_validated; }; #endif //FEATHER_CREATEWALLET_H diff --git a/src/wizard/PageWalletFile.ui b/src/wizard/PageWalletFile.ui new file mode 100644 index 0000000..16490fb --- /dev/null +++ b/src/wizard/PageWalletFile.ui @@ -0,0 +1,130 @@ + + + PageWalletFile + + + + 0 + 0 + 486 + 317 + + + + Create Wallet + + + + + + Name + + + + + + + + + false + + + + + + + Change + + + + + + + + + + + + Directory + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Choose a name and directory for your wallet files. + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + label_9 + label + verticalSpacer + line_walletName + frame + + + line_walletName + line_walletDir + btnChange + + + + diff --git a/src/wizard/PageWalletRestoreKeys.cpp b/src/wizard/PageWalletRestoreKeys.cpp new file mode 100644 index 0000000..364213f --- /dev/null +++ b/src/wizard/PageWalletRestoreKeys.cpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "WalletWizard.h" +#include "PageWalletRestoreKeys.h" +#include "ui_PageWalletRestoreKeys.h" + +#include + +PageWalletRestoreKeys::PageWalletRestoreKeys(AppContext *ctx, WizardFields *fields, QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::PageWalletRestoreKeys) + , m_ctx(ctx) + , m_fields(fields) +{ + ui->setupUi(this); + this->setTitle("Restore wallet from keys"); + ui->label_errorString->hide(); + + QPixmap pixmap = QPixmap(":/assets/images/key.png"); + ui->icon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation)); + +#ifndef QT_NO_CURSOR + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + QGuiApplication::restoreOverrideCursor(); +#endif + + if (m_ctx->networkType == NetworkType::Type::MAINNET) { + ui->line_address->setPlaceholderText("4..."); + } else if (m_ctx->networkType == NetworkType::Type::STAGENET) { + ui->line_address->setPlaceholderText("5..."); + } +} + +void PageWalletRestoreKeys::initializePage() { + ui->line_address->setText(""); + ui->line_viewkey->setText(""); + ui->line_spendkey->setText(""); +} + +int PageWalletRestoreKeys::nextId() const { + return WalletWizard::Page_SetRestoreHeight; +} + +bool PageWalletRestoreKeys::validatePage() { + auto errStyle = "QLineEdit{border: 1px solid red;}"; + + ui->line_address->setStyleSheet(""); + ui->line_viewkey->setStyleSheet(""); + ui->label_errorString->hide(); + + QString address = ui->line_address->text().trimmed(); + QString viewkey = ui->line_viewkey->text().trimmed(); + QString spendkey = ui->line_spendkey->text().trimmed(); + + if(!m_ctx->walletManager->addressValid(address, m_ctx->networkType)){ + ui->label_errorString->show(); + ui->label_errorString->setText("Invalid address."); + ui->line_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->line_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->line_viewkey->setStyleSheet(errStyle); + return false; + } + + m_fields->address = address; + m_fields->secretViewKey = viewkey; + m_fields->secretSpendKey = spendkey; + return true; +} diff --git a/src/wizard/viewonlywallet.h b/src/wizard/PageWalletRestoreKeys.h similarity index 58% rename from src/wizard/viewonlywallet.h rename to src/wizard/PageWalletRestoreKeys.h index 32f4836..2122ca8 100644 --- a/src/wizard/viewonlywallet.h +++ b/src/wizard/PageWalletRestoreKeys.h @@ -13,23 +13,25 @@ #include "appcontext.h" namespace Ui { - class ViewOnlyPage; + class PageWalletRestoreKeys; } -class ViewOnlyPage : public QWizardPage +class PageWalletRestoreKeys : public QWizardPage { Q_OBJECT public: - explicit ViewOnlyPage(AppContext *ctx, QWidget *parent = nullptr); + explicit PageWalletRestoreKeys(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr); + void initializePage() override; bool validatePage() override; int nextId() const override; - void cleanupPage() const; private: + void resetWidgets(); + AppContext *m_ctx; - QLabel *topLabel; - Ui::ViewOnlyPage *ui; + WizardFields *m_fields; + Ui::PageWalletRestoreKeys *ui; }; #endif diff --git a/src/wizard/PageWalletRestoreKeys.ui b/src/wizard/PageWalletRestoreKeys.ui new file mode 100644 index 0000000..b321cb3 --- /dev/null +++ b/src/wizard/PageWalletRestoreKeys.ui @@ -0,0 +1,224 @@ + + + PageWalletRestoreKeys + + + + 0 + 0 + 759 + 460 + + + + ViewOnlyPage + + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + To restore a view-only wallet leave the spend key blank. + + + true + + + + + + + + + + Primary address + + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Secret view key + + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Secret spend key (optional) + + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + errorString + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + diff --git a/src/wizard/PageWalletRestoreSeed.cpp b/src/wizard/PageWalletRestoreSeed.cpp new file mode 100644 index 0000000..75938be --- /dev/null +++ b/src/wizard/PageWalletRestoreSeed.cpp @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "WalletWizard.h" +#include "PageWalletRestoreSeed.h" +#include "ui_PageWalletRestoreSeed.h" + +#include +#include +#include + +#include // tevador 14 word +#include "utils/FeatherSeed.h" +#include "globals.h" + +PageWalletRestoreSeed::PageWalletRestoreSeed(AppContext *ctx, WizardFields *fields, QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::PageWalletRestoreSeed) + , m_ctx(ctx) + , m_fields(fields) +{ + ui->setupUi(this); + ui->label_errorString->hide(); + + QStringList bip39English; + for (int i = 0; i != 2048; i++) + bip39English << QString::fromStdString(wordlist::english.get_word(i)); + // Restore has limited error correction capability, namely it can correct a single erasure + // (illegible word with a known location). This can be tested by replacing a word with xxxx + bip39English << "xxxx"; + + QByteArray data = Utils::fileOpen(":/assets/mnemonic_25_english.txt"); + QStringList moneroEnglish; + for (const auto &seed_word: data.split('\n')) + moneroEnglish << seed_word; + + m_tevador.length = 14; + m_tevador.setWords(bip39English); + + m_legacy.length = 25; + m_legacy.setWords(moneroEnglish); + + ui->seedEdit->setAcceptRichText(false); + ui->seedEdit->setMaximumHeight(150); + +#ifndef QT_NO_CURSOR + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + QGuiApplication::restoreOverrideCursor(); +#endif + + connect(ui->seedBtnGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &PageWalletRestoreSeed::onSeedTypeToggled); + + this->onSeedTypeToggled(); +} + +void PageWalletRestoreSeed::onSeedTypeToggled() { + if (ui->radio14->isChecked()) { + m_mode = &m_tevador; + m_fields->seedType = SeedType::TEVADOR; + ui->seedEdit->setPlaceholderText("Enter 14 word seed.."); + } + else if (ui->radio25->isChecked()) { + m_mode = &m_legacy; + m_fields->seedType = SeedType::MONERO; + ui->seedEdit->setPlaceholderText("Enter 25 word seed.."); + } + + ui->label_errorString->hide(); + ui->seedEdit->setStyleSheet(""); + ui->seedEdit->setCompleter(&m_mode->completer); + ui->seedEdit->setText(""); +} + +int PageWalletRestoreSeed::nextId() const { + if (m_mode == &m_legacy) { + return WalletWizard::Page_SetRestoreHeight; + } + + return WalletWizard::Page_WalletFile; +} + +void PageWalletRestoreSeed::initializePage() { + this->setTitle(m_fields->modeText); + ui->seedEdit->setText(""); + ui->seedEdit->setStyleSheet(""); + ui->label_errorString->hide(); + ui->line_seedOffset->setText(""); +} + +bool PageWalletRestoreSeed::validatePage() { + ui->label_errorString->hide(); + ui->seedEdit->setStyleSheet(""); + + auto errStyle = "QTextEdit{border: 1px solid red;}"; + auto seed = ui->seedEdit->toPlainText().replace("\n", "").replace("\r", "").trimmed(); + auto seedSplit = seed.split(" "); + + if (seedSplit.length() != m_mode->length) { + ui->label_errorString->show(); + ui->label_errorString->setText(QString("The mnemonic seed should be %1 words.").arg(m_mode->length)); + ui->seedEdit->setStyleSheet(errStyle); + return false; + } + + for (const auto &word : seedSplit) { + if (!m_mode->words.contains(word)) { + ui->label_errorString->show(); + ui->label_errorString->setText(QString("Mnemonic seed contains an unknown word: %1").arg(word)); + ui->seedEdit->setStyleSheet(errStyle); + return false; + } + } + + auto _seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], QString::fromStdString(globals::coinName), m_ctx->seedLanguage, seedSplit); + if (!_seed.errorString.isEmpty()) { + QMessageBox::warning(this, "Invalid seed", QString("Invalid seed:\n\n%1").arg(_seed.errorString)); + ui->seedEdit->setStyleSheet(errStyle); + return false; + } + if (!_seed.correction.isEmpty()) { + QMessageBox::information(this, "Corrected erasure", QString("xxxx -> %1").arg(_seed.correction)); + } + + m_fields->seed = seed; + m_fields->seedOffsetPassphrase = ui->line_seedOffset->text(); + + return true; +} diff --git a/src/wizard/PageWalletRestoreSeed.h b/src/wizard/PageWalletRestoreSeed.h new file mode 100644 index 0000000..d29c7b2 --- /dev/null +++ b/src/wizard/PageWalletRestoreSeed.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#ifndef FEATHER_WIZARDRESTORE_H +#define FEATHER_WIZARDRESTORE_H + +#include +#include +#include +#include +#include + +#include "utils/textedit.h" +#include "appcontext.h" + +namespace Ui { + class PageWalletRestoreSeed; +} + +class PageWalletRestoreSeed : public QWizardPage +{ + Q_OBJECT + +public: + explicit PageWalletRestoreSeed(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr); + bool validatePage() override; + void initializePage() override; + int nextId() const override; + +private: + struct seedType { + seedType() + { + completer.setModel(&completerModel); + completer.setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer.setCaseSensitivity(Qt::CaseInsensitive); + completer.setWrapAround(false); + } + + void setWords(const QStringList &wordlist) { + this->words = wordlist; + completerModel.setStringList(words); + } + + int length; + QStringList words; + QStringListModel completerModel; + QCompleter completer; + }; + + void onSeedTypeToggled(); + + AppContext *m_ctx; + Ui::PageWalletRestoreSeed *ui; + WizardFields *m_fields; + + seedType m_tevador; + seedType m_legacy; + + seedType *m_mode; +}; + +#endif diff --git a/src/wizard/PageWalletRestoreSeed.ui b/src/wizard/PageWalletRestoreSeed.ui new file mode 100644 index 0000000..7f3f5c5 --- /dev/null +++ b/src/wizard/PageWalletRestoreSeed.ui @@ -0,0 +1,123 @@ + + + PageWalletRestoreSeed + + + + 0 + 0 + 874 + 639 + + + + RestorePage + + + + + + Select seed type: + + + + + + 14 word mnemonic seed + + + true + + + seedBtnGroup + + + + + + + 25 word mnemonic seed + + + seedBtnGroup + + + + + + + + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Seed offset passphrase: + + + + + + + QLineEdit::Normal + + + Leave blank if in doubt + + + + + + + errorString + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + TextEdit + QTextEdit +
utils/textedit.h
+
+
+ + + + + +
diff --git a/src/wizard/createwalletseed.cpp b/src/wizard/PageWalletSeed.cpp similarity index 57% rename from src/wizard/createwalletseed.cpp rename to src/wizard/PageWalletSeed.cpp index 1e538b5..87b5489 100644 --- a/src/wizard/createwalletseed.cpp +++ b/src/wizard/PageWalletSeed.cpp @@ -1,24 +1,23 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "wizard/createwalletseed.h" -#include "wizard/walletwizard.h" -#include "ui_createwalletseed.h" +#include "WalletWizard.h" +#include "PageWalletSeed.h" +#include "ui_PageWalletSeed.h" +#include "globals.h" -#include #include -CreateWalletSeedPage::CreateWalletSeedPage(AppContext *ctx, QWidget *parent) : - QWizardPage(parent), - m_ctx(ctx), - ui(new Ui::CreateWalletSeedPage) { +PageWalletSeed::PageWalletSeed(AppContext *ctx, WizardFields *fields, QWidget *parent) + : QWizardPage(parent) + , m_ctx(ctx) + , ui(new Ui::PageWalletSeed) + , m_fields(fields) +{ ui->setupUi(this); - this->setFinalPage(true); - this->setTitle("Wallet seed"); - // hide ui element, we only need it for registerField - this->registerField("mnemonicSeed", ui->hiddenMnemonicSeed); - ui->hiddenMnemonicSeed->hide(); + QPixmap pixmap = QPixmap(":/assets/images/seed.png"); + ui->seedIcon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation)); ui->seedWord2->setHelpText("In addition to the private spend key, Tevador's 14 word seed scheme also encodes the " "restore date, cryptocurrency type, and reserves a few bits for future use. " @@ -30,15 +29,14 @@ CreateWalletSeedPage::CreateWalletSeedPage(AppContext *ctx, QWidget *parent) : connect(ui->btnCopy, &QPushButton::clicked, [this]{ Utils::copyToClipboard(m_mnemonic); }); - - this->setButtonText(QWizard::FinishButton, "Create/Open wallet"); } -void CreateWalletSeedPage::initializePage() { +void PageWalletSeed::initializePage() { this->generateSeed(); + this->setTitle(m_fields->modeText); } -void CreateWalletSeedPage::seedRoulette(int count) { +void PageWalletSeed::seedRoulette(int count) { count += 1; if (count > m_rouletteSpin) return; @@ -50,14 +48,18 @@ void CreateWalletSeedPage::seedRoulette(int count) { }); } -void CreateWalletSeedPage::generateSeed() { - FeatherSeed seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage); - m_mnemonic = seed.mnemonic.join(" "); - m_restoreHeight = seed.restoreHeight; +void PageWalletSeed::generateSeed() { + do { + FeatherSeed seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], + QString::fromStdString(globals::coinName), m_ctx->seedLanguage); + m_mnemonic = seed.mnemonic.join(" "); + m_restoreHeight = seed.restoreHeight; + } while (m_mnemonic.split(" ").length() != 14); // https://github.com/tevador/monero-seed/issues/2 + this->displaySeed(m_mnemonic); } -void CreateWalletSeedPage::displaySeed(const QString &seed){ +void PageWalletSeed::displaySeed(const QString &seed){ QStringList seedSplit = seed.split(" "); ui->seedWord1->setText(seedSplit[0]); @@ -76,13 +78,13 @@ void CreateWalletSeedPage::displaySeed(const QString &seed){ ui->seedWord14->setText(seedSplit[13]); } -int CreateWalletSeedPage::nextId() const { - return -1; +int PageWalletSeed::nextId() const { + return WalletWizard::Page_WalletFile; } -bool CreateWalletSeedPage::validatePage() { - if(m_mnemonic.isEmpty()) return false; - if(!m_restoreHeight) return false; +bool PageWalletSeed::validatePage() { + if (m_mnemonic.isEmpty()) return false; + if (!m_restoreHeight) return false; QMessageBox seedWarning(this); seedWarning.setWindowTitle("Warning!"); @@ -90,11 +92,15 @@ bool CreateWalletSeedPage::validatePage() { "• Never type it on a website\n" "• Store it safely (offline)\n" "• Do not lose your seed!"); + seedWarning.addButton("Go back", QMessageBox::RejectRole); seedWarning.addButton("I understand", QMessageBox::AcceptRole); - seedWarning.exec(); + int res = seedWarning.exec(); + + if (res == QMessageBox::Rejected) { + return false; + } + + m_fields->seed = m_mnemonic; - this->setField("mnemonicSeed", m_mnemonic); - this->setField("restoreHeight", m_restoreHeight); - emit createWallet(); return true; } \ No newline at end of file diff --git a/src/wizard/createwalletseed.h b/src/wizard/PageWalletSeed.h similarity index 77% rename from src/wizard/createwalletseed.h rename to src/wizard/PageWalletSeed.h index 986068d..dcb4f8d 100644 --- a/src/wizard/createwalletseed.h +++ b/src/wizard/PageWalletSeed.h @@ -12,15 +12,15 @@ #include "appcontext.h" namespace Ui { - class CreateWalletSeedPage; + class PageWalletSeed; } -class CreateWalletSeedPage : public QWizardPage +class PageWalletSeed : public QWizardPage { Q_OBJECT public: - explicit CreateWalletSeedPage(AppContext *ctx, QWidget *parent = nullptr); + explicit PageWalletSeed(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr); void initializePage() override; bool validatePage() override; int nextId() const override; @@ -37,8 +37,9 @@ signals: private: AppContext *m_ctx; - QLabel *topLabel; - Ui::CreateWalletSeedPage *ui; + Ui::PageWalletSeed *ui; + + WizardFields *m_fields; QString m_mnemonic; int m_restoreHeight; diff --git a/src/wizard/PageWalletSeed.ui b/src/wizard/PageWalletSeed.ui new file mode 100644 index 0000000..291f48a --- /dev/null +++ b/src/wizard/PageWalletSeed.ui @@ -0,0 +1,714 @@ + + + PageWalletSeed + + + + 0 + 0 + 877 + 408 + + + + WizardPage + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + + Store your 14-word wallet seed in a safe location. + + + true + + + + + + + This seed will allow you to recover your wallet in case of computer failure. + + + true + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 1. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 4. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 7. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 10. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 13. + + + + + + + TextLabel + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 2. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 5. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 8. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 11. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 14. + + + + + + + TextLabel + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 3. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 6. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 9. + + + + + + + TextLabel + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 7 + + + 7 + + + + + + 0 + 0 + + + + 12. + + + + + + + TextLabel + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::NoFocus + + + Copy + + + + + + + Qt::NoFocus + + + Generate + + + + + + + + + + HelpLabel + QLabel +
components.h
+
+
+ + +
diff --git a/src/wizard/WalletWizard.cpp b/src/wizard/WalletWizard.cpp new file mode 100644 index 0000000..2f12bcb --- /dev/null +++ b/src/wizard/WalletWizard.cpp @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "utils/utils.h" + +#include "WalletWizard.h" +#include "PageMenu.h" +#include "PageOpenWallet.h" +#include "PageWalletFile.h" +#include "PageNetwork.h" +#include "PageWalletSeed.h" +#include "PageWalletRestoreSeed.h" +#include "PageWalletRestoreKeys.h" +#include "PageSetPassword.h" +#include "PageSetRestoreHeight.h" +#include "globals.h" + +#include +#include +#include + +WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidget *parent) + : QWizard(parent) + , m_ctx(ctx) +{ + this->setWindowTitle("Welcome to Feather Wallet"); + this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png")); + + m_walletKeysFilesModel = new WalletKeysFilesModel(m_ctx, this); + m_walletKeysFilesModel->refresh(); + + auto menuPage = new PageMenu(m_ctx, &m_wizardFields, m_walletKeysFilesModel, this); + auto openWalletPage = new PageOpenWallet(m_ctx, m_walletKeysFilesModel, this); + auto createWallet = new PageWalletFile(m_ctx, &m_wizardFields , this); + auto createWalletSeed = new PageWalletSeed(m_ctx, &m_wizardFields, this); + auto walletSetPasswordPage = new PageSetPassword(m_ctx, &m_wizardFields, this); + setPage(Page_Menu, menuPage); + setPage(Page_WalletFile, createWallet); + setPage(Page_OpenWallet, openWalletPage); + setPage(Page_CreateWalletSeed, createWalletSeed); + setPage(Page_SetPasswordPage, walletSetPasswordPage); + setPage(Page_Network, new PageNetwork(m_ctx, this)); + setPage(Page_WalletRestoreSeed, new PageWalletRestoreSeed(m_ctx, &m_wizardFields, this)); + setPage(Page_WalletRestoreKeys, new PageWalletRestoreKeys(m_ctx, &m_wizardFields, this)); + setPage(Page_SetRestoreHeight, new PageSetRestoreHeight(m_ctx, &m_wizardFields, this)); + + + setStartId(Page_Menu); + + setButtonText(QWizard::CancelButton, "Close"); + setPixmap(QWizard::WatermarkPixmap, QPixmap(":/assets/images/banners/3.png")); + setWizardStyle(WizardStyle::ModernStyle); + setOption(QWizard::NoBackButtonOnStartPage); + + connect(this, &QWizard::rejected, [=]{ + return QApplication::exit(1); + }); + + connect(menuPage, &PageMenu::enableDarkMode, [this](bool enable){ + if (enable) + emit skinChanged("QDarkStyle"); + else + emit skinChanged("Native"); + }); + + connect(walletSetPasswordPage, &PageSetPassword::createWallet, this, &WalletWizard::createWallet); + connect(createWallet, &PageWalletFile::defaultWalletDirChanged, [this](const QString &walletDir){ + emit defaultWalletDirChanged(walletDir); + }); + + connect(openWalletPage, &PageOpenWallet::openWallet, [=](const QString &path){ + const auto walletPassword = this->field("walletPassword").toString(); + emit openWallet(path, walletPassword); + }); +} + +void WalletWizard::createWallet() { + auto walletPath = QString("%1/%2").arg(m_wizardFields.walletDir, m_wizardFields.walletName); + + if (m_wizardFields.mode == WizardMode::RestoreFromKeys) { + m_ctx->createWalletFromKeys(walletPath, + m_wizardFields.password, + m_wizardFields.address, + m_wizardFields.secretViewKey, + m_wizardFields.secretSpendKey, + m_wizardFields.restoreHeight); + return; + } + + auto seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], QString::fromStdString(globals::coinName), m_ctx->seedLanguage, m_wizardFields.seed.split(" ")); + + if (m_wizardFields.mode == WizardMode::RestoreFromSeed && m_wizardFields.seedType == SeedType::MONERO) + seed.setRestoreHeight(m_wizardFields.restoreHeight); + + m_ctx->createWallet(seed, walletPath, m_wizardFields.password, m_wizardFields.seedOffsetPassphrase); +} diff --git a/src/wizard/WalletWizard.h b/src/wizard/WalletWizard.h new file mode 100644 index 0000000..076a362 --- /dev/null +++ b/src/wizard/WalletWizard.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#ifndef WALLETWIZARD_H +#define WALLETWIZARD_H + +#include +#include +#include + +#include "appcontext.h" +#include "utils/RestoreHeightLookup.h" +#include "utils/config.h" + +enum WizardMode { + CreateWallet = 0, + OpenWallet, + RestoreFromSeed, + RestoreFromKeys +}; + +struct WizardFields { + QString walletName; + QString walletDir; + QString seed; + QString seedOffsetPassphrase; + QString password; + QString modeText; + QString address; + QString secretViewKey; + QString secretSpendKey; + WizardMode mode; + int restoreHeight; + SeedType seedType; +}; + +class WalletWizard : public QWizard +{ + Q_OBJECT + +public: + enum Page { + Page_Menu, + Page_WalletFile, + Page_CreateWalletSeed, + Page_SetPasswordPage, + Page_OpenWallet, + Page_Network, + Page_WalletRestoreSeed, + Page_WalletRestoreKeys, + Page_SetRestoreHeight + }; + + explicit WalletWizard(AppContext *ctx, WalletWizard::Page startPage = WalletWizard::Page::Page_Menu, QWidget *parent = nullptr); + +signals: + void skinChanged(const QString &skin); + void openWallet(QString path, QString password); + void defaultWalletDirChanged(QString walletDir); + +private: + AppContext *m_ctx; + WalletKeysFilesModel *m_walletKeysFilesModel; + WizardFields m_wizardFields; + + void createWallet(); +}; + +#endif // WALLETWIZARD_H diff --git a/src/wizard/createwallet.cpp b/src/wizard/createwallet.cpp deleted file mode 100644 index e9359d6..0000000 --- a/src/wizard/createwallet.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#include "utils/utils.h" -#include "wizard/createwallet.h" -#include "wizard/walletwizard.h" -#include "ui_createwallet.h" - -#include -#include - -CreateWalletPage::CreateWalletPage(AppContext *ctx, QWidget *parent) : - QWizardPage(parent), - ui(new Ui::CreateWalletPage), - m_ctx(ctx) { - ui->setupUi(this); - this->setTitle("Create wallet"); - this->setButtonText(QWizard::FinishButton, "Open wallet"); - - // hide ui element, we only need it for registerField - this->registerField("walletName*", ui->walletName); - this->registerField("walletDirectory", ui->directory); - this->registerField("walletPasswd", ui->password); - this->registerField("walletPath", ui->walletPath); - ui->walletPath->hide(); - - ui->directory->setText(m_ctx->defaultWalletDir); - m_walletDir = m_ctx->defaultWalletDir; - connect(ui->btnChange, &QPushButton::clicked, [=] { - QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly); - if(walletDir.isEmpty()) return; - m_ctx->defaultWalletDir = walletDir; - m_ctx->defaultWalletDirRoot = walletDir; - ui->directory->setText(walletDir); - config()->set(Config::walletDirectory, walletDir); - emit defaultWalletDirChanged(walletDir); - }); - - connect(ui->directory, &QLineEdit::textChanged, [=](const QString &data) { - m_walletDir = data; - this->validateWidgets(); - }); - - connect(ui->walletName, &QLineEdit::textChanged, [=](QString data) { - this->validateWidgets(); - }); -} - -void CreateWalletPage::initializePage() { - ui->directory->setText(m_ctx->defaultWalletDir); -} - -bool CreateWalletPage::validateWidgets(){ - ui->walletName->setStyleSheet(""); - ui->directory->setStyleSheet(""); - auto walletPass = ui->password->text(); - auto errStyle = "QLineEdit{border: 1px solid red;}"; - if(m_walletDir.isEmpty()){ - ui->walletName->setStyleSheet(errStyle); - ui->directory->setStyleSheet(errStyle); - return false; - } - - if(!Utils::dirExists(m_walletDir)) { - ui->walletName->setStyleSheet(errStyle); - ui->directory->setStyleSheet(errStyle); - return false; - } - - ui->directory->setStyleSheet(""); - auto walletName = ui->walletName->text().replace(".keys", ""); - if(walletName.isEmpty()) { - ui->walletName->setStyleSheet(errStyle); - return false; - } - - auto walletPath = QDir(m_walletDir).filePath(walletName + ".keys"); - if(Utils::fileExists(walletPath)) { - ui->walletName->setStyleSheet(errStyle); - return false; - } - - return true; -} - -int CreateWalletPage::nextId() const { - auto restoredSeed = this->field("mnemonicRestoredSeed").toString(); - auto restoredViewOnlyKey = this->field("viewOnlyViewKey").toString(); - - if(!restoredSeed.isEmpty() || !restoredViewOnlyKey.isEmpty()) - return -1; - - return WalletWizard::Page_CreateWalletSeed; -} - -bool CreateWalletPage::validatePage() { - if(!this->validateWidgets()) return false; - auto walletName = ui->walletName->text().replace(".keys", ""); - auto walletPath = QDir(m_walletDir).filePath(walletName + ".keys"); - this->setField("walletPath", walletPath); - ui->walletName->setStyleSheet(""); - - auto restoredSeed = this->field("mnemonicRestoredSeed").toString(); - auto restoredViewOnlyKey = this->field("viewOnlyViewKey").toString(); - if(!restoredSeed.isEmpty() || !restoredViewOnlyKey.isEmpty()) emit createWallet(); - return true; -} diff --git a/src/wizard/createwallet.ui b/src/wizard/createwallet.ui deleted file mode 100644 index 912fb43..0000000 --- a/src/wizard/createwallet.ui +++ /dev/null @@ -1,113 +0,0 @@ - - - CreateWalletPage - - - - 0 - 0 - 400 - 321 - - - - Create Wallet - - - - - - - - - QLineEdit::Password - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - - - - - - Directory - - - - - - - Password (Optional) - - - - - - - Name - - - - - - - - - - - - Change directory - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - label_3 - label_9 - password - directory - label - verticalSpacer - walletName - walletPath - horizontalLayoutWidget - - - walletName - walletPath - password - directory - - - - diff --git a/src/wizard/createwalletseed.ui b/src/wizard/createwalletseed.ui deleted file mode 100644 index 90949de..0000000 --- a/src/wizard/createwalletseed.ui +++ /dev/null @@ -1,530 +0,0 @@ - - - CreateWalletSeedPage - - - - 0 - 0 - 529 - 510 - - - - WizardPage - - - - 0 - - - - - - - - - 1 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 5 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 9 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 13 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - - - - - 2 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 6 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 10 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 14 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - - - - - 3 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 7 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 11 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - - - - - - - - - - - - 4 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 8 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - 12 - - - - 5 - - - 6 - - - 5 - - - 5 - - - - - TextLabel - - - - - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::NoFocus - - - Copy - - - - - - - Qt::NoFocus - - - Generate - - - - - - - - - Qt::NoFocus - - - - - - - - 0 - 0 - - - - Please save these 14 words on paper. This seed will allow you to recover your wallet in case of computer failure. - - - true - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - HelpLabel - QLabel -
components.h
-
-
- - -
diff --git a/src/wizard/menu.cpp b/src/wizard/menu.cpp deleted file mode 100644 index 9daefea..0000000 --- a/src/wizard/menu.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#include "wizard/menu.h" -#include "wizard/walletwizard.h" -#include "ui_menu.h" - -#include - -MenuPage::MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent) - : QWizardPage(parent) - , ui(new Ui::MenuPage) - , m_ctx(ctx) - , m_walletKeysFilesModel(wallets) -{ - ui->setupUi(this); - this->setButtonText(QWizard::FinishButton, "Open recent wallet"); -} - -void MenuPage::initializePage() { - if (m_walletKeysFilesModel->rowCount() > 0) { - ui->radioOpen->setChecked(true); - } else { - ui->radioCreate->setChecked(true); - } -} - -int MenuPage::nextId() const { - if (ui->radioOpen->isChecked()) - return WalletWizard::Page_OpenWallet; - if (ui->radioCreate->isChecked()) - return WalletWizard::Page_CreateWallet; - if(ui->radioSeed->isChecked()) - return WalletWizard::Page_Restore; - if(ui->radioViewOnly->isChecked()) - return WalletWizard::Page_ViewOnly; - return 0; -} - -bool MenuPage::validatePage() { - return true; -} \ No newline at end of file diff --git a/src/wizard/restorewallet.cpp b/src/wizard/restorewallet.cpp deleted file mode 100644 index 866517f..0000000 --- a/src/wizard/restorewallet.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#include "wizard/restorewallet.h" -#include "wizard/walletwizard.h" -#include "ui_restorewallet.h" - -#include -#include -#include - -#include // tevador 14 word -#include "utils/FeatherSeed.h" - -RestorePage::RestorePage(AppContext *ctx, QWidget *parent) : - QWizardPage(parent), - ui(new Ui::RestorePage), - m_ctx(ctx) { - ui->setupUi(this); - this->setTitle("Restore wallet"); - ui->restoreFrame->hide(); - ui->label_errorString->hide(); - - QFont f("feather"); - f.setStyleHint(QFont::Monospace); - - auto data = Utils::fileOpen(":/assets/mnemonic_25_english.txt"); - for(const auto &seed_word: data.split('\n')) - m_words25 << seed_word; - for(int i = 0; i != 2048; i++) - m_words14 << QString::fromStdString(wordlist::english.get_word(i)); - - // Restore has limited error correction capability, namely it can correct a single erasure - // (illegible word with a known location). This can be tested by replacing a word with xxxx - m_words14 << "xxxx"; - - // - m_completer14Model = new QStringListModel(m_words14, m_completer14); - m_completer14 = new QCompleter(this); - m_completer14->setModel(m_completer14Model); - m_completer14->setModelSorting(QCompleter::CaseInsensitivelySortedModel); - m_completer14->setCaseSensitivity(Qt::CaseInsensitive); - m_completer14->setWrapAround(false); - // - m_completer25Model = new QStringListModel(m_words25, m_completer25); - m_completer25 = new QCompleter(this); - m_completer25->setModel(m_completer25Model); - m_completer25->setModelSorting(QCompleter::CaseInsensitivelySortedModel); - m_completer25->setCaseSensitivity(Qt::CaseInsensitive); - m_completer25->setWrapAround(false); - // - ui->seedEdit->setCompleter(m_completer14); - ui->seedEdit->setAcceptRichText(false); - ui->seedEdit->setMaximumHeight(80); - ui->seedEdit->setFrameShape(QFrame::Box); - ui->seedEdit->setFrameShadow(QFrame::Plain); - ui->seedEdit->setFont(f); - ui->seedEdit->setPlaceholderText("Insert your mnemonic 14 word seed..."); - // - - auto *dummyRestoredSeed = new QLineEdit(this); - dummyRestoredSeed->setVisible(false); - auto *restoreHeightEdit = new QLineEdit(this); - restoreHeightEdit->setVisible(false); - - this->registerField("mnemonicRestoredSeed", dummyRestoredSeed); - this->registerField("restoreHeight", restoreHeightEdit); - -#ifndef QT_NO_CURSOR - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - QGuiApplication::restoreOverrideCursor(); -#endif - - connect(ui->seedBtnGroup, QOverload::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *button){ - auto name = button->objectName(); - if(name == "radio25") { - m_mode = 25; - ui->label_errorString->hide(); - ui->seedEdit->setStyleSheet(""); - ui->seedEdit->setCompleter(m_completer25); - ui->restoreFrame->show(); - ui->seedEdit->setPlaceholderText("Insert your mnemonic 25 word seed..."); - } else if (name == "radio14") { - m_mode = 14; - ui->label_errorString->hide(); - ui->seedEdit->setStyleSheet(""); - ui->seedEdit->setCompleter(m_completer14); - ui->restoreFrame->hide(); - ui->seedEdit->setPlaceholderText("Insert your mnemonic 14 word seed..."); - } - - ui->seedEdit->setText(""); - }); - - if(m_ctx->networkType == NetworkType::Type::TESTNET) { - ui->restoreHeightWidget->hideSlider(); - } else { - // load restoreHeight lookup db - ui->restoreHeightWidget->initRestoreHeights(m_ctx->restoreHeights[m_ctx->networkType]); - } -} - -int RestorePage::nextId() const { - return WalletWizard::Page_CreateWallet; -} - -void RestorePage::initializePage() { - ui->seedEdit->setText(""); - ui->restoreHeightWidget->setHeight(1); -} - -bool RestorePage::validatePage() { - ui->label_errorString->hide(); - auto errStyle = "QTextEdit{border: 1px solid red;}"; - int restoreHeight = ui->restoreHeightWidget->getHeight(); - auto seed = ui->seedEdit->toPlainText().replace("\n", "").replace("\r", "").trimmed(); - auto seedSplit = seed.split(" "); - - if(m_mode == 14) { - if(seedSplit.length() != 14) { - ui->label_errorString->show(); - ui->label_errorString->setText("The mnemonic seed should be 14 words."); - ui->seedEdit->setStyleSheet(errStyle); - return false; - } - - for(const auto &word: seedSplit) { - if(!m_words14.contains(word)) { - ui->label_errorString->show(); - ui->label_errorString->setText(QString("Mnemonic seed contains an unknown word: %1").arg(word)); - ui->seedEdit->setStyleSheet(errStyle); - return false; - } - } - } else if(m_mode == 25) { - if(seedSplit.length() != 25) { - ui->label_errorString->show(); - ui->label_errorString->setText("The mnemonic seed should be 25 words."); - ui->seedEdit->setStyleSheet(errStyle); - return false; - } - - for(const auto &word: seedSplit) { - if(!m_words25.contains(word)) { - ui->label_errorString->show(); - ui->label_errorString->setText(QString("Mnemonic seed contains an unknown word: %1").arg(word)); - ui->seedEdit->setStyleSheet(errStyle); - return false; - } - } - } - - auto _seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage, seedSplit); - if (!_seed.errorString.isEmpty()) { - QMessageBox::warning(this, "Invalid seed", QString("Invalid seed:\n\n%1").arg(_seed.errorString)); - ui->seedEdit->setStyleSheet(errStyle); - return false; - } - if (!_seed.correction.isEmpty()) { - QMessageBox::information(this, "Corrected erasure", QString("xxxx -> %1").arg(_seed.correction)); - } - - restoreHeight = _seed.restoreHeight; - - this->setField("restoreHeight", restoreHeight); - this->setField("mnemonicSeed", seed); - this->setField("mnemonicRestoredSeed", seed); - return true; -} diff --git a/src/wizard/restorewallet.h b/src/wizard/restorewallet.h deleted file mode 100644 index 4774f0a..0000000 --- a/src/wizard/restorewallet.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#ifndef FEATHER_WIZARDRESTORE_H -#define FEATHER_WIZARDRESTORE_H - -#include -#include -#include -#include -#include - -#include "utils/textedit.h" -#include "appcontext.h" - -namespace Ui { - class RestorePage; -} - -class RestorePage : public QWizardPage -{ - Q_OBJECT - -public: - explicit RestorePage(AppContext *ctx, QWidget *parent = nullptr); - bool validatePage() override; - void initializePage() override; - int nextId() const override; - -private: - AppContext *m_ctx; - Ui::RestorePage *ui; - - int m_mode = 14; - QStringList m_words14; - QStringList m_words25; - QStringListModel *m_completer14Model = nullptr; - QStringListModel *m_completer25Model = nullptr; - QCompleter *m_completer14 = nullptr; - QCompleter *m_completer25 = nullptr; -}; - -#endif diff --git a/src/wizard/restorewallet.ui b/src/wizard/restorewallet.ui deleted file mode 100644 index dbd9122..0000000 --- a/src/wizard/restorewallet.ui +++ /dev/null @@ -1,172 +0,0 @@ - - - RestorePage - - - - 0 - 0 - 502 - 506 - - - - RestorePage - - - - - - 14 word mnemonic seed - - - true - - - seedBtnGroup - - - - - - - 25 word mnemonic seed - - - seedBtnGroup - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - false - - - - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - You may specify the "restore height". This is the moment you first created your wallet, 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 -
- - TextEdit - QTextEdit -
utils/textedit.h
-
-
- - - - - -
diff --git a/src/wizard/viewonlywallet.cpp b/src/wizard/viewonlywallet.cpp deleted file mode 100644 index 01c610b..0000000 --- a/src/wizard/viewonlywallet.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#include "wizard/viewonlywallet.h" -#include "wizard/walletwizard.h" -#include "ui_viewonlywallet.h" - -#include - -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(); - - 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.ui b/src/wizard/viewonlywallet.ui deleted file mode 100644 index 426d78c..0000000 --- a/src/wizard/viewonlywallet.ui +++ /dev/null @@ -1,119 +0,0 @@ - - - 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 deleted file mode 100644 index 63b0219..0000000 --- a/src/wizard/walletwizard.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#include "utils/utils.h" -#include "wizard/walletwizard.h" -#include "wizard/menu.h" -#include "wizard/openwallet.h" -#include "wizard/createwallet.h" -#include "wizard/network.h" -#include "wizard/createwalletseed.h" -#include "wizard/restorewallet.h" -#include "wizard/viewonlywallet.h" - -#include -#include -#include - -WalletWizard::WalletWizard(AppContext *ctx, WalletWizard::Page startPage, QWidget *parent) - : QWizard(parent) - , m_ctx(ctx) -{ - this->setWindowTitle("Welcome to Feather Wallet"); - this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png")); - - m_walletKeysFilesModel = new WalletKeysFilesModel(m_ctx, this); - m_walletKeysFilesModel->refresh(); - - auto openWalletPage = new OpenWalletPage(m_ctx, m_walletKeysFilesModel, this); - auto createWallet = new CreateWalletPage(m_ctx, this); - auto createWalletSeed = new CreateWalletSeedPage(m_ctx, this); - setPage(Page_Menu, new MenuPage(m_ctx, m_walletKeysFilesModel, this)); - setPage(Page_CreateWallet, createWallet); - setPage(Page_OpenWallet, openWalletPage); - 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); - else - setStartId(Page_Menu); - - setButtonText(QWizard::CancelButton, "Close"); - setPixmap(QWizard::WatermarkPixmap, QPixmap(":/assets/images/banners/3.png")); - setWizardStyle(WizardStyle::ModernStyle); - setOption(QWizard::NoBackButtonOnStartPage); - - connect(this, &QWizard::rejected, [=]{ - return QApplication::exit(1); - }); - - connect(createWalletSeed, &CreateWalletSeedPage::createWallet, this, &WalletWizard::createWallet); - connect(createWallet, &CreateWalletPage::createWallet, this, &WalletWizard::createWallet); - connect(createWallet, &CreateWalletPage::defaultWalletDirChanged, [this](const QString &walletDir){ - emit defaultWalletDirChanged(walletDir); - }); - - connect(openWalletPage, &OpenWalletPage::openWallet, [=](const QString &path){ - const auto walletPasswd = this->field("walletPasswd").toString(); - emit openWallet(path, walletPasswd); - }); -} - -void WalletWizard::createWallet() { - auto mnemonicRestoredSeed = this->field("mnemonicRestoredSeed").toString(); - auto mnemonicSeed = mnemonicRestoredSeed.isEmpty() ? this->field("mnemonicSeed").toString() : mnemonicRestoredSeed; - 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(); - - if(!viewKey.isEmpty() && !viewAddress.isEmpty()) { - auto viewHeight = this->field("viewOnlyHeight").toUInt(); - m_ctx->createWalletViewOnly(walletPath, - walletPasswd, - viewAddress, - viewKey, spendKey, viewHeight); - return; - } - - auto seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage, mnemonicSeed.split(" ")); - - if(restoreHeight > 0) - seed.setRestoreHeight(restoreHeight); - m_ctx->createWallet(seed, walletPath, walletPasswd); -} diff --git a/src/wizard/walletwizard.h b/src/wizard/walletwizard.h deleted file mode 100644 index 3d3b885..0000000 --- a/src/wizard/walletwizard.h +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#ifndef WALLETWIZARD_H -#define WALLETWIZARD_H - -#include -#include -#include - -#include "appcontext.h" -#include "utils/RestoreHeightLookup.h" -#include "utils/config.h" - -class WalletWizard : public QWizard -{ - Q_OBJECT - -public: - 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: - void openWallet(QString path, QString password); - void defaultWalletDirChanged(QString walletDir); - -private: - AppContext *m_ctx; - WalletKeysFilesModel *m_walletKeysFilesModel; - - void createWallet(); -}; - -#endif // WALLETWIZARD_H