Wizard: rework

This commit is contained in:
tobtoht 2021-03-12 19:26:48 +01:00
parent cc5b3c3c27
commit 6741e684f5
No known key found for this signature in database
GPG key ID: 1CADD27F41F45C3C
63 changed files with 2781 additions and 1783 deletions

View file

@ -29,7 +29,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_VERBOSE_MAKEFILE ON)
endif() endif()
set(MONERO_HEAD "aa0f58570d412cf02dd325e567bbc9fa093df16c") set(MONERO_HEAD "e175e02b9b8d289bccab3ff0f3fd70c4dbf8c71f")
set(BUILD_GUI_DEPS ON) set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64") set(ARCH "x86-64")
set(BUILD_64 ON) set(BUILD_64 ON)

2
monero

@ -1 +1 @@
Subproject commit aa0f58570d412cf02dd325e567bbc9fa093df16c Subproject commit e175e02b9b8d289bccab3ff0f3fd70c4dbf8c71f

View file

@ -26,84 +26,39 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->networkClearnet = new QNetworkAccessManager(); this->networkClearnet = new QNetworkAccessManager();
this->cmdargs = cmdargs; this->cmdargs = cmdargs;
#if defined(Q_OS_MAC) this->isTorSocks = Utils::isTorsocks();
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->isTails = TailsOS::detect(); this->isTails = TailsOS::detect();
this->isWhonix = WhonixOS::detect(); this->isWhonix = WhonixOS::detect();
//Paths // ----------------- Setup 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";
}
QFileInfo appImageDir(appImagePath); QString configDir = Config::defaultConfigDir().path();
return appImageDir.absoluteDir().path() + "/.feather"; createConfigDirectory(configDir);
}();
if (QDir().mkpath(portablePath)) {
this->configRoot = portablePath;
} else {
qCritical() << "Unable to create portable directory: " << portablePath;
}
}
this->accountName = Utils::getUnixAccountName();
this->homeDir = QDir::homePath();
QString walletDir = config()->get(Config::walletDirectory).toString(); QString walletDir = config()->get(Config::walletDirectory).toString();
if (walletDir.isEmpty()) { if (walletDir.isEmpty()) {
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) walletDir = Utils::defaultWalletDir();
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;
} }
this->defaultWalletDir = walletDir;
// Create wallet dirs
if (!QDir().mkpath(defaultWalletDir)) if (!QDir().mkpath(defaultWalletDir))
qCritical() << "Unable to create dir: " << defaultWalletDir; qCritical() << "Unable to create dir: " << defaultWalletDir;
this->configDirectory = QString("%1/.config/feather/").arg(this->configRoot); // ----------------- Network Type -----------------
#if defined(Q_OS_UNIX)
if(!this->configDirectory.endsWith('/'))
this->configDirectory = QString("%1/").arg(this->configDirectory);
#endif
// Config if (this->cmdargs->isSet("stagenet"))
createConfigDirectory(this->configDirectory);
if(this->cmdargs->isSet("stagenet"))
this->networkType = NetworkType::STAGENET; this->networkType = NetworkType::STAGENET;
else if(this->cmdargs->isSet("testnet")) else if (this->cmdargs->isSet("testnet"))
this->networkType = NetworkType::TESTNET; this->networkType = NetworkType::TESTNET;
else else
this->networkType = NetworkType::MAINNET; this->networkType = NetworkType::MAINNET;
// auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt();
// AppContext::nodeSource = static_cast<NodeSource>(nodeSourceUInt);
this->nodes = new Nodes(this, this->networkClearnet); this->nodes = new Nodes(this, this->networkClearnet);
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged); connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes); connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
// Tor & socks proxy // 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); connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
// Store the wallet every 2 minutes // Store the wallet every 2 minutes
@ -117,7 +72,7 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// price history lookup // price history lookup
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey(); 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(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase);
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){ connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8(); QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
@ -133,18 +88,18 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// XMRig // XMRig
#ifdef HAS_XMRIG #ifdef HAS_XMRIG
this->XMRig = new XmRig(this->configDirectory, this); this->XMRig = new XmRig(configDir, this);
this->XMRig->prepare(); this->XMRig->prepare();
#endif #endif
this->walletManager = WalletManager::instance(); this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDirectory); QString logPath = QString("%1/daemon.log").arg(configDir);
Monero::Utils::onStartup(); Monero::Utils::onStartup();
Monero::Wallet::init("", "feather", logPath.toStdString(), true); Monero::Wallet::init("", "feather", logPath.toStdString(), true);
bool logLevelFromEnv; bool logLevelFromEnv;
int logLevel = qEnvironmentVariableIntValue("MONERO_LOG_LEVEL", &logLevelFromEnv); int logLevel = qEnvironmentVariableIntValue("MONERO_LOG_LEVEL", &logLevelFromEnv);
if(this->cmdargs->isSet("quiet")) if (this->cmdargs->isSet("quiet"))
this->walletManager->setLogLevel(-1); this->walletManager->setLogLevel(-1);
else if (logLevelFromEnv && logLevel >= 0 && logLevel <= Monero::WalletManagerFactory::LogLevel_Max) else if (logLevelFromEnv && logLevel >= 0 && logLevel <= Monero::WalletManagerFactory::LogLevel_Max)
Monero::WalletManagerFactory::setLogLevel(logLevel); Monero::WalletManagerFactory::setLogLevel(logLevel);
@ -159,12 +114,13 @@ void AppContext::initTor() {
this->tor = new Tor(this, this); this->tor = new Tor(this, this);
this->tor->start(); this->tor->start();
if (!(isWhonix)) { if (!(isWhonix) && !(isTorSocks)) {
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy); this->network->setProxy(*networkProxy);
if (m_wsUrl.host().endsWith(".onion")) if (globals::websocketUrl.host().endsWith(".onion")) {
this->ws->webSocket.setProxy(*networkProxy); this->ws->webSocket.setProxy(*networkProxy);
} }
}
} }
void AppContext::initWS() { void AppContext::initWS() {
@ -218,9 +174,9 @@ void AppContext::onCreateTransaction(const QString &address, quint64 amount, con
qDebug() << "creating tx"; qDebug() << "creating tx";
if (all) if (all)
this->currentWallet->createTransactionAllAsync(address, "", this->tx_mixin, this->tx_priority); this->currentWallet->createTransactionAllAsync(address, "", globals::mixin, this->tx_priority);
else else
this->currentWallet->createTransactionAsync(address, "", amount, this->tx_mixin, this->tx_priority); this->currentWallet->createTransactionAsync(address, "", amount, globals::mixin, this->tx_priority);
emit initiateTransaction(); emit initiateTransaction();
} }
@ -335,7 +291,6 @@ void AppContext::onWalletOpened(Wallet *wallet) {
this->refreshed = false; this->refreshed = false;
this->currentWallet = wallet; this->currentWallet = wallet;
this->walletPath = this->currentWallet->path() + ".keys"; this->walletPath = this->currentWallet->path() + ".keys";
this->walletViewOnly = this->currentWallet->viewOnly();
config()->set(Config::walletPath, this->walletPath); config()->set(Config::walletPath, this->walletPath);
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent); connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
@ -368,7 +323,7 @@ void AppContext::onWalletOpened(Wallet *wallet) {
void AppContext::setWindowTitle(bool mining) { void AppContext::setWindowTitle(bool mining) {
QFileInfo fileInfo(this->walletPath); QFileInfo fileInfo(this->walletPath);
auto title = QString("Feather - [%1]").arg(fileInfo.fileName()); auto title = QString("Feather - [%1]").arg(fileInfo.fileName());
if(this->walletViewOnly) if(this->currentWallet && this->currentWallet->viewOnly())
title += " [view-only]"; title += " [view-only]";
if(mining) if(mining)
title += " [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)) { if(Utils::fileExists(path)) {
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path); auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
qCritical() << err; qCritical() << err;
@ -551,11 +506,12 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
Wallet *wallet = nullptr; Wallet *wallet = nullptr;
if (seed.seedType == SeedType::TEVADOR) { 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.seed", seed.mnemonic.join(" "));
wallet->setCacheAttribute("feather.seedoffset", seedOffset);
} }
if (seed.seedType == SeedType::MONERO) { 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; this->currentWallet = wallet;
@ -567,7 +523,7 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
this->createWalletFinish(password); 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)) { if(Utils::fileExists(path)) {
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path); auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
qCritical() << err; qCritical() << err;
@ -674,17 +630,18 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) {
} }
void AppContext::donateBeg() { void AppContext::donateBeg() {
if(this->currentWallet == nullptr) return; if (this->currentWallet == nullptr) return;
if(this->networkType != NetworkType::Type::MAINNET) return; if (this->networkType != NetworkType::Type::MAINNET) return;
if(this->currentWallet->viewOnly()) return; if (this->currentWallet->viewOnly()) return;
auto donationCounter = config()->get(Config::donateBeg).toInt(); auto donationCounter = config()->get(Config::donateBeg).toInt();
if(donationCounter == -1) if(donationCounter == -1)
return; // previously donated return; // previously donated
donationCounter += 1; donationCounter += 1;
if (donationCounter % m_donationBoundary == 0) if (donationCounter % globals::donationBoundary == 0) {
emit donationNag(); emit donationNag();
}
config()->set(Config::donateBeg, donationCounter); config()->set(Config::donateBeg, donationCounter);
} }
@ -763,7 +720,7 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q
void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) { void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) {
for (auto &addr : address) { for (auto &addr : address) {
if (addr == this->donationAddress) { if (addr == globals::donationAddress) {
this->donationSending = true; this->donationSending = true;
} }
} }

View file

@ -37,41 +37,25 @@ Q_OBJECT
public: public:
explicit AppContext(QCommandLineParser *cmdargs); explicit AppContext(QCommandLineParser *cmdargs);
~AppContext() override; ~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; QCommandLineParser *cmdargs;
QString coinName = "monero"; bool isTails = false;
bool isWhonix = false;
bool isTorSocks = false; bool isTorSocks = false;
QString homeDir;
QString accountName; bool donationSending = false;
QString configRoot;
QString configDirectory;
QString defaultWalletDir; QString defaultWalletDir;
QString defaultWalletDirRoot;
QString tmpTxDescription; QString tmpTxDescription;
QString walletPath; QString walletPath;
QString walletPassword = ""; QString walletPassword = "";
bool walletViewOnly = false;
NetworkType::Type networkType; NetworkType::Type networkType;
QString applicationPath;
static void createConfigDirectory(const QString &dir) ;
QMap<QString, int> heights; QMap<QString, int> heights;
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights; QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
const quint64 kdfRounds = 1;
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
quint32 tx_mixin = static_cast<const quint32 &>(10);
QString seedLanguage = "English"; // 14 word `monero-seed` only has English QString seedLanguage = "English"; // 14 word `monero-seed` only has English
QNetworkAccessManager *network; QNetworkAccessManager *network;
@ -88,12 +72,14 @@ public:
static QMap<QString, QString> txCache; static QMap<QString, QString> txCache;
static TxFiatHistory *txFiatHistory; static TxFiatHistory *txFiatHistory;
static void createConfigDirectory(const QString &dir);
// libwalletqt // libwalletqt
bool refreshed = false; bool refreshed = false;
WalletManager *walletManager; WalletManager *walletManager;
Wallet *currentWallet = nullptr; Wallet *currentWallet = nullptr;
void createWallet(FeatherSeed seed, const QString &path, const QString &password); void createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset = "");
void createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight); 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 createWalletFinish(const QString &password);
void syncStatusUpdated(quint64 height, quint64 target); void syncStatusUpdated(quint64 height, quint64 target);
void updateBalance(); void updateBalance();
@ -176,9 +162,7 @@ signals:
void setTitle(const QString &title); // set window title void setTitle(const QString &title); // set window title
private: private:
const int m_donationBoundary = 15;
QTimer m_storeTimer; QTimer m_storeTimer;
QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
}; };
#endif //FEATHER_APPCONTEXT_H #endif //FEATHER_APPCONTEXT_H

View file

@ -321,6 +321,9 @@
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="17" column="0"> <item row="17" column="0">

View file

@ -13,8 +13,9 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
ui->label_restoreHeight->setText(QString::number(wallet->getWalletCreationHeight())); ui->label_restoreHeight->setText(QString::number(wallet->getWalletCreationHeight()));
QString seedOffset = wallet->getCacheAttribute("feather.seedoffset");
QString seed_14_words = wallet->getCacheAttribute("feather.seed"); 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()) { if (seed_14_words.isEmpty()) {
ui->check_toggleSeedType->hide(); ui->check_toggleSeedType->hide();
@ -24,6 +25,9 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
ui->frameRestoreHeight->setVisible(false); 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){ connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed_14_words](bool toggled){
this->setSeed(toggled ? seed_25_words : seed_14_words); this->setSeed(toggled ? seed_25_words : seed_14_words);
ui->frameRestoreHeight->setVisible(toggled); ui->frameRestoreHeight->setVisible(toggled);

View file

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>519</width> <width>519</width>
<height>330</height> <height>543</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -63,6 +63,32 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QFrame" name="frameSeedOffset">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Seed offset:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_seedOffset">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QFrame" name="frameRestoreHeight"> <widget class="QFrame" name="frameRestoreHeight">
<property name="frameShape"> <property name="frameShape">

View file

@ -5,10 +5,23 @@
#define FEATHER_GLOBALS_H #define FEATHER_GLOBALS_H
#include <QtGlobal> #include <QtGlobal>
#include <QUrl>
namespace globals namespace globals
{ {
// coin constants
const std::string coinName = "monero";
const qreal cdiv = 1e12; 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 #endif //FEATHER_GLOBALS_H

View file

@ -35,9 +35,9 @@ Wallet::ConnectionStatus Wallet::connectionStatus() const
return m_connectionStatus; 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 QString Wallet::getSeedLanguage() const

View file

@ -76,7 +76,6 @@ class Wallet : public QObject, public PassprasePrompter
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged) Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged) Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged)
Q_PROPERTY(QString seed READ getSeed)
Q_PROPERTY(QString seedLanguage READ getSeedLanguage) Q_PROPERTY(QString seedLanguage READ getSeedLanguage)
Q_PROPERTY(Status status READ status) Q_PROPERTY(Status status READ status)
Q_PROPERTY(NetworkType::Type nettype READ nettype) Q_PROPERTY(NetworkType::Type nettype READ nettype)
@ -127,7 +126,7 @@ public:
ConnectionStatus connectionStatus() const; ConnectionStatus connectionStatus() const;
//! returns mnemonic seed //! returns mnemonic seed
QString getSeed() const; QString getSeed(const QString &seedOffset) const;
//! returns seed language //! returns seed language
QString getSeedLanguage() const; QString getSeedLanguage() const;

View file

@ -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, 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); QMutexLocker locker(&m_mutex);
if (m_currentWallet) { if (m_currentWallet) {
@ -151,7 +151,7 @@ Wallet *WalletManager::createDeterministicWalletFromSpendKey(const QString &path
m_currentWallet = NULL; m_currentWallet = NULL;
} }
Monero::Wallet * w = m_pimpl->createDeterministicWalletFromSpendKey(path.toStdString(), "", language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, Monero::Wallet * w = m_pimpl->createDeterministicWalletFromSpendKey(path.toStdString(), "", language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
spendkey.toStdString(), kdfRounds); spendkey.toStdString(), kdfRounds, offset_passphrase.toStdString());
m_currentWallet = new Wallet(w); m_currentWallet = new Wallet(w);
return m_currentWallet; return m_currentWallet;
} }

View file

@ -76,7 +76,8 @@ public:
NetworkType::Type nettype, NetworkType::Type nettype,
const QString &spendkey, const QString &spendkey,
quint64 restoreHeight, quint64 restoreHeight,
quint64 kdfRounds); quint64 kdfRounds,
const QString &offset_passphrase = "");
Q_INVOKABLE Wallet * createWalletFromDevice(const QString &path, Q_INVOKABLE Wallet * createWalletFromDevice(const QString &path,
const QString &password, const QString &password,

View file

@ -51,9 +51,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
QCommandLineOption torPortOption(QStringList() << "tor-port", "Port of running Tor instance.", "torPort"); QCommandLineOption torPortOption(QStringList() << "tor-port", "Port of running Tor instance.", "torPort");
parser.addOption(torPortOption); parser.addOption(torPortOption);
QCommandLineOption debugModeOption(QStringList() << "debug", "Run program in debug mode.");
parser.addOption(debugModeOption);
QCommandLineOption quietModeOption(QStringList() << "quiet", "Limit console output"); QCommandLineOption quietModeOption(QStringList() << "quiet", "Limit console output");
parser.addOption(quietModeOption); parser.addOption(quietModeOption);
@ -94,7 +91,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
} }
const QStringList args = parser.positionalArguments(); const QStringList args = parser.positionalArguments();
bool debugMode = parser.isSet(debugModeOption);
bool localTor = parser.isSet(useLocalTorOption); bool localTor = parser.isSet(useLocalTorOption);
bool stagenet = parser.isSet(stagenetOption); bool stagenet = parser.isSet(stagenetOption);
bool testnet = parser.isSet(testnetOption); bool testnet = parser.isSet(testnetOption);
@ -111,8 +107,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
QCoreApplication::setOrganizationName("featherwallet.org"); QCoreApplication::setOrganizationName("featherwallet.org");
auto *ctx = new AppContext(&parser); auto *ctx = new AppContext(&parser);
ctx->applicationPath = QString(argv[0]);
ctx->isDebug = debugMode;
auto *cli = new CLI(ctx, &cli_app); auto *cli = new CLI(ctx, &cli_app);
QObject::connect(cli, &CLI::closeApplication, &cli_app, &QCoreApplication::quit); QObject::connect(cli, &CLI::closeApplication, &cli_app, &QCoreApplication::quit);
@ -162,8 +156,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
} }
auto *ctx = new AppContext(&parser); auto *ctx = new AppContext(&parser);
ctx->applicationPath = QString(argv[0]);
ctx->isDebug = debugMode;
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
// For some odd reason, if we don't do this, QPushButton's // For some odd reason, if we don't do this, QPushButton's

View file

@ -503,6 +503,7 @@ void MainWindow::initWidgets() {
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){ WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
auto *wizard = new WalletWizard(m_ctx, startPage, this); 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::openWallet, m_ctx, &AppContext::onOpenWallet);
connect(wizard, &WalletWizard::defaultWalletDirChanged, m_windowSettings, &Settings::updatePaths); connect(wizard, &WalletWizard::defaultWalletDirChanged, m_windowSettings, &Settings::updatePaths);
connect(wizard, &WalletWizard::rejected, [this]{ connect(wizard, &WalletWizard::rejected, [this]{
@ -544,7 +545,7 @@ void MainWindow::touchbarShowWallet() {
void MainWindow::onWalletCreatedError(const QString &err) { void MainWindow::onWalletCreatedError(const QString &err) {
QMessageBox::warning(this, "Wallet creation error", 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) { void MainWindow::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
@ -1015,11 +1016,11 @@ void MainWindow::closeEvent(QCloseEvent *event) {
} }
void MainWindow::donateButtonClicked() { 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) if (donation <= 0)
donation = 0.1337; 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); ui->tabWidget->setCurrentIndex(Tabs::SEND);
} }

View file

@ -28,7 +28,7 @@
#include "utils/networking.h" #include "utils/networking.h"
#include "appcontext.h" #include "appcontext.h"
#include "utils/config.h" #include "utils/config.h"
#include "wizard/walletwizard.h" #include "wizard/WalletWizard.h"
#include "settings.h" #include "settings.h"
#include "dialog/aboutdialog.h" #include "dialog/aboutdialog.h"
#include "dialog/signverifydialog.h" #include "dialog/signverifydialog.h"

View file

@ -71,7 +71,6 @@ Settings::Settings(QWidget *parent) :
QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly); QString walletDir = QFileDialog::getExistingDirectory(this, "Select wallet directory ", m_ctx->defaultWalletDir, QFileDialog::ShowDirsOnly);
if (walletDir.isEmpty()) return; if (walletDir.isEmpty()) return;
m_ctx->defaultWalletDir = walletDir; m_ctx->defaultWalletDir = walletDir;
m_ctx->defaultWalletDirRoot = walletDir;
config()->set(Config::walletDirectory, walletDir); config()->set(Config::walletDirectory, walletDir);
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir); ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
}); });
@ -81,8 +80,8 @@ Settings::Settings(QWidget *parent) :
void Settings::updatePaths() { void Settings::updatePaths() {
ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir); ui->lineEdit_defaultWalletDir->setText(m_ctx->defaultWalletDir);
ui->lineEdit_configDir->setText(m_ctx->configDirectory); ui->lineEdit_configDir->setText(Config::defaultConfigDir().path());
ui->lineEdit_applicationDir->setText(m_ctx->applicationPath); ui->lineEdit_applicationDir->setText(QCoreApplication::applicationDirPath());
} }
void Settings::fiatCurrencySelected(int index) { void Settings::fiatCurrencySelected(int index) {

View file

@ -23,7 +23,10 @@ struct RestoreHeightLookup {
// will calculate the blockheight based off the last known // will calculate the blockheight based off the last known
// date: ((now - lastKnownDate) / blockTime) - clearance // date: ((now - lastKnownDate) / blockTime) - clearance
if(this->type == NetworkType::TESTNET) return 1; if (this->type == NetworkType::TESTNET) {
return 1;
}
int blockTime = 120; int blockTime = 120;
int blocksPerDay = 86400 / blockTime; int blocksPerDay = 86400 / blockTime;
int blockCalcClearance = blocksPerDay * 5; int blockCalcClearance = blocksPerDay * 5;
@ -50,12 +53,20 @@ struct RestoreHeightLookup {
// @TODO: most likely inefficient, refactor // @TODO: most likely inefficient, refactor
QMap<int, int>::iterator i; QMap<int, int>::iterator i;
int timestamp = 0; int timestamp = 0;
int heightData = 1;
for (i = this->data.begin(); i != this->data.end(); ++i) { for (i = this->data.begin(); i != this->data.end(); ++i) {
int ts = i.key(); int ts = i.key();
if (i.value() > height) if (i.value() > height)
return timestamp; return timestamp;
timestamp = ts; timestamp = ts;
heightData = i.value();
} }
while (heightData < height) {
heightData += 720; // blocks per day
timestamp += 86400; // seconds in day
}
return timestamp; return timestamp;
} }
@ -67,7 +78,7 @@ struct RestoreHeightLookup {
for(const auto &line: data.split('\n')) { for(const auto &line: data.split('\n')) {
if(line.trimmed().isEmpty()) continue; if(line.trimmed().isEmpty()) continue;
auto spl = line.trimmed().split(':'); 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; return rtn;
} }

View file

@ -106,29 +106,45 @@ Config::Config(const QString& fileName, QObject* parent)
Config::Config(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(); bool isTails = TailsOS::detect();
configPath = QDir::homePath();
if (isTails) { // #if defined(PORTABLE) if (isTails) { // #if defined(PORTABLE)
QString appImagePath = qgetenv("APPIMAGE"); QString appImagePath = qgetenv("APPIMAGE");
QFileInfo appImageDir(appImagePath); QFileInfo appImageDir(appImagePath);
QDir portablePath(appImageDir.absoluteDir().path() + "/.feather"); QDir portablePath(appImageDir.absoluteDir().path() + "/feather_data");
if (portablePath.mkpath(".")) { if (portablePath.mkpath(".")) {
configPath = portablePath.path(); configDir = portablePath;
} } else {
else {
qCritical() << "Unable to create portable directory: " << portablePath.path(); qCritical() << "Unable to create portable directory: " << portablePath.path();
} }
} }
configPath += "/.config/feather/settings.json"; QString configPath = configDir.filePath("settings.json");
init(QDir::toNativeSeparators(configPath)); 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() Config::~Config()
{ {

View file

@ -9,6 +9,7 @@
#include <QObject> #include <QObject>
#include <QSettings> #include <QSettings>
#include <QPointer> #include <QPointer>
#include <QDir>
class Config : public QObject class Config : public QObject
{ {
@ -50,7 +51,8 @@ public:
redditFrontend, redditFrontend,
showHistorySyncNotice, showHistorySyncNotice,
GUI_HistoryViewState, GUI_HistoryViewState,
amountPrecision amountPrecision,
portableMode
}; };
~Config() override; ~Config() override;
@ -60,6 +62,9 @@ public:
void sync(); void sync();
void resetToDefaults(); void resetToDefaults();
static QDir defaultConfigDir();
static QDir defaultPortableConfigDir();
static Config* instance(); static Config* instance();
signals: signals:

View file

@ -69,14 +69,14 @@ void WalletKeysFilesModel::refresh() {
void WalletKeysFilesModel::updateDirectories() { void WalletKeysFilesModel::updateDirectories() {
this->walletDirectories.clear(); this->walletDirectories.clear();
this->walletDirectories << m_ctx->defaultWalletDirRoot; this->walletDirectories << m_ctx->defaultWalletDir; // TODO
auto walletPath = config()->get(Config::walletPath).toString(); auto walletPath = config()->get(Config::walletPath).toString();
if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) { if(!walletPath.isEmpty() && Utils::fileExists(walletPath)) {
QDir d = QFileInfo(walletPath).absoluteDir(); QDir d = QFileInfo(walletPath).absoluteDir();
this->walletDirectories << d.absolutePath(); this->walletDirectories << d.absolutePath();
} }
this->walletDirectories << m_ctx->homeDir; this->walletDirectories << QDir::homePath();
this->walletDirectories.removeDuplicates(); this->walletDirectories.removeDuplicates();
} }
@ -160,12 +160,13 @@ QVariant WalletKeysFilesModel::data(const QModelIndex &index, int role) const {
return QString("main"); return QString("main");
} }
case ModelColumns::FileName: case ModelColumns::FileName:
return walletKeyFile.fileName(); return walletKeyFile.fileName().replace(".keys", "");
case ModelColumns::Path: { case ModelColumns::Path: {
auto fp = walletKeyFile.path(); auto fp = walletKeyFile.path();
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
if (fp.startsWith(m_ctx->homeDir)) if (fp.startsWith(QDir::homePath())) {
fp = QString("~/%1").arg(fp.remove(0, m_ctx->homeDir.length() + 1)); fp = QString("~/%1").arg(fp.remove(0, QDir::homePath().length() + 1));
}
#endif #endif
return fp; return fp;
} }

View file

@ -20,7 +20,7 @@ Tor::Tor(AppContext *ctx, QObject *parent)
{ {
connect(m_checkConnectionTimer, &QTimer::timeout, this, &Tor::checkConnection); 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"); this->torDataPath = QDir(this->torDir).filePath("data");
if (m_ctx->cmdargs->isSet("tor-port")) { if (m_ctx->cmdargs->isSet("tor-port")) {

View file

@ -469,3 +469,21 @@ QTextCharFormat Utils::addressTextFormat(const SubaddressIndex &index) {
} }
return QTextCharFormat(); 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
}

View file

@ -76,6 +76,8 @@ public:
static QMap<QString, QLocale> localeCache; static QMap<QString, QLocale> localeCache;
static QString balanceFormat(quint64 balance); static QString balanceFormat(quint64 balance);
static QTextCharFormat addressTextFormat(const SubaddressIndex &index); static QTextCharFormat addressTextFormat(const SubaddressIndex &index);
static bool isTorsocks();
static QString defaultWalletDir();
template<typename QEnum> template<typename QEnum>
static QString QtEnumToString (const QEnum value) static QString QtEnumToString (const QEnum value)

69
src/wizard/PageMenu.cpp Normal file
View file

@ -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 <QFileDialog>
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;
}

View file

@ -11,23 +11,27 @@
#include "appcontext.h" #include "appcontext.h"
namespace Ui { namespace Ui {
class MenuPage; class PageMenu;
} }
class MenuPage : public QWizardPage class PageMenu : public QWizardPage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit MenuPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr); explicit PageMenu(AppContext *ctx, WizardFields *fields, WalletKeysFilesModel *wallets, QWidget *parent = nullptr);
void initializePage() override; void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
signals:
void enableDarkMode(bool enable);
private: private:
AppContext *m_ctx; AppContext *m_ctx;
WalletKeysFilesModel *m_walletKeysFilesModel; WalletKeysFilesModel *m_walletKeysFilesModel;
Ui::MenuPage *ui; Ui::PageMenu *ui;
WizardFields *m_fields;
}; };
#endif //FEATHER_WIZARDMENU_H #endif //FEATHER_WIZARDMENU_H

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>MenuPage</class> <class>PageMenu</class>
<widget class="QWizardPage" name="MenuPage"> <widget class="QWizardPage" name="PageMenu">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
@ -63,7 +63,7 @@
<enum>Qt::ClickFocus</enum> <enum>Qt::ClickFocus</enum>
</property> </property>
<property name="text"> <property name="text">
<string>Import from keys</string> <string>Restore wallet from keys</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -80,6 +80,17 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QCheckBox" name="check_darkMode">
<property name="text">
<string>Dark mode</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="enabled"> <property name="enabled">
@ -101,6 +112,10 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
</layout>
</item>
</layout>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View file

@ -1,23 +1,21 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project. // Copyright (c) 2020-2021, The Monero Project.
#include "wizard/network.h" #include "PageNetwork.h"
#include "ui_network.h" #include "ui_PageNetwork.h"
#include <QFileDialog> #include <QFileDialog>
NetworkPage::NetworkPage(AppContext *ctx, QWidget *parent) : // Unused for now
PageNetwork::PageNetwork(AppContext *ctx, QWidget *parent) :
QWizardPage(parent), QWizardPage(parent),
ui(new Ui::NetworkPage), ui(new Ui::PageNetwork),
m_ctx(ctx) { m_ctx(ctx) {
ui->setupUi(this); ui->setupUi(this);
this->setTitle("Welcome to Feather!"); this->setTitle("Welcome to Feather!");
ui->customFrame->hide(); 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"); 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(); 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; return 0;
} }
bool NetworkPage::validatePage() { bool PageNetwork::validatePage() {
auto cfg = config()->get(Config::nodeSource); auto cfg = config()->get(Config::nodeSource);
if(ui->radioRemote->isChecked()) { if(ui->radioRemote->isChecked()) {
if(cfg != NodeSource::websocket) if(cfg != NodeSource::websocket)

View file

@ -12,22 +12,22 @@
#include "utils/nodes.h" #include "utils/nodes.h"
namespace Ui { namespace Ui {
class NetworkPage; class PageNetwork;
} }
class NetworkPage : public QWizardPage class PageNetwork : public QWizardPage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit NetworkPage(AppContext *ctx, QWidget *parent = nullptr); explicit PageNetwork(AppContext *ctx, QWidget *parent = nullptr);
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
private: private:
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel; QLabel *topLabel;
Ui::NetworkPage *ui; Ui::PageNetwork *ui;
}; };
#endif //FEATHER_WIZARDNETWORK_H #endif //FEATHER_WIZARDNETWORK_H

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>NetworkPage</class> <class>PageNetwork</class>
<widget class="QWizardPage" name="NetworkPage"> <widget class="QWizardPage" name="PageNetwork">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>

View file

@ -1,15 +1,15 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project. // Copyright (c) 2020-2021, The Monero Project.
#include "wizard/openwallet.h" #include "PageOpenWallet.h"
#include "ui_openwallet.h" #include "ui_PageOpenWallet.h"
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent) PageOpenWallet::PageOpenWallet(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent)
: QWizardPage(parent) : QWizardPage(parent)
, ui(new Ui::OpenWalletPage) , ui(new Ui::PageOpenWallet)
, m_ctx(ctx) , m_ctx(ctx)
, m_walletKeysFilesModel(wallets) , m_walletKeysFilesModel(wallets)
{ {
@ -18,7 +18,7 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, Q
connect(ui->btnBrowse, &QPushButton::clicked, [=]{ connect(ui->btnBrowse, &QPushButton::clicked, [=]{
// manually browsing for wallet // manually browsing for wallet
auto walletPath = config()->get(Config::walletPath).toString(); auto walletPath = config()->get(Config::walletPath).toString();
if(walletPath.isEmpty()) if (walletPath.isEmpty())
walletPath = m_ctx->defaultWalletDir; walletPath = m_ctx->defaultWalletDir;
QString path = QFileDialog::getOpenFileName(this, "Select your wallet file", walletPath, "Wallet file (*.keys)"); QString path = QFileDialog::getOpenFileName(this, "Select your wallet file", walletPath, "Wallet file (*.keys)");
if(path.isEmpty()) return; if(path.isEmpty()) return;
@ -29,9 +29,7 @@ OpenWalletPage::OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, Q
return; 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)); config()->set(Config::autoOpenWalletPath, QString("%1%2").arg(m_ctx->networkType).arg(path));
emit openWallet(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){ connect(ui->walletTable->selectionModel(), &QItemSelectionModel::currentRowChanged, [this](QModelIndex current, QModelIndex prev){
this->updatePath(); 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(); m_walletKeysFilesModel->refresh();
} }
void OpenWalletPage::updatePath() { void PageOpenWallet::updatePath() {
QModelIndex index = ui->walletTable->currentIndex(); QModelIndex index = ui->walletTable->currentIndex();
if (!index.isValid()) { if (!index.isValid()) {
ui->labelPath->clear(); ui->linePath->clear();
return; return;
} }
QString path = index.model()->data(index.siblingAtColumn(WalletKeysFilesModel::Path), Qt::DisplayRole).toString(); 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; return -1;
} }
bool OpenWalletPage::validatePage() { bool PageOpenWallet::validatePage() {
QModelIndex index = ui->walletTable->currentIndex(); QModelIndex index = ui->walletTable->currentIndex();
if(!index.isValid()) { if(!index.isValid()) {
QMessageBox::warning(this, "Wallet not selected", "Please select a wallet from the list."); QMessageBox::warning(this, "Wallet not selected", "Please select a wallet from the list.");

View file

@ -12,15 +12,15 @@
#include "utils/keysfiles.h" #include "utils/keysfiles.h"
namespace Ui { namespace Ui {
class OpenWalletPage; class PageOpenWallet;
} }
class OpenWalletPage : public QWizardPage class PageOpenWallet : public QWizardPage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OpenWalletPage(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr); explicit PageOpenWallet(AppContext *ctx, WalletKeysFilesModel *wallets, QWidget *parent = nullptr);
void initializePage() override; void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
@ -34,7 +34,7 @@ private:
AppContext *m_ctx; AppContext *m_ctx;
WalletKeysFilesModel *m_walletKeysFilesModel; WalletKeysFilesModel *m_walletKeysFilesModel;
WalletKeysFilesProxyModel *m_keysProxy; WalletKeysFilesProxyModel *m_keysProxy;
Ui::OpenWalletPage *ui; Ui::PageOpenWallet *ui;
QStandardItemModel *m_model; QStandardItemModel *m_model;
}; };

View file

@ -1,19 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>OpenWalletPage</class> <class>PageOpenWallet</class>
<widget class="QWizardPage" name="OpenWalletPage"> <widget class="QWizardPage" name="PageOpenWallet">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>729</width> <width>706</width>
<height>414</height> <height>440</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>WizardPage</string> <string>WizardPage</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="linePath">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTreeView" name="walletTable"> <widget class="QTreeView" name="walletTable">
<property name="rootIsDecorated"> <property name="rootIsDecorated">
@ -24,7 +42,7 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QCheckBox" name="openOnStartup"> <widget class="QCheckBox" name="openOnStartup">
@ -46,24 +64,16 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="btnBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="labelPath">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>walletTable</tabstop>
<tabstop>linePath</tabstop>
<tabstop>btnBrowse</tabstop>
<tabstop>openOnStartup</tabstop>
</tabstops>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View file

@ -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();
}

View file

@ -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 <QWizardPage>
#include <QWidget>
#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

View file

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageSetPassword</class>
<widget class="QWizardPage" name="PageSetPassword">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>431</width>
<height>231</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Choose a password to encrypt your wallet keys.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Password:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Confirm Password:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_confirmPassword">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,110 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#include <QValidator>
#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();
}

View file

@ -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 <QWizardPage>
#include <QWidget>
#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

View file

@ -0,0 +1,279 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageSetRestoreHeight</class>
<widget class="QWizardPage" name="PageSetRestoreHeight">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>676</width>
<height>447</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Enter the wallet creation date or set the restore height manually.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Wallet creation date:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="line_creationDate">
<property name="placeholderText">
<string>YYYY-MM-DD</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Restore height:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="line_restoreHeight"/>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Specifying a restore height that is too high may result in an inaccurate balance.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame_walletAgeWarning">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="infoIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Wallet is very old. Synchronization may take a long time.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_scanWarning">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="warningIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_scanWarning">
<property name="text">
<string>Wallet will not scan for transactions before YYYY/MM/DD.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -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 <QFileDialog>
#include <QMessageBox>
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)));
}

View file

@ -7,32 +7,37 @@
#include <QLabel> #include <QLabel>
#include <QWizardPage> #include <QWizardPage>
#include <QWidget> #include <QWidget>
#include <QDir>
#include "appcontext.h" #include "appcontext.h"
namespace Ui { namespace Ui {
class CreateWalletPage; class PageWalletFile;
} }
class CreateWalletPage : public QWizardPage class PageWalletFile : public QWizardPage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CreateWalletPage(AppContext *ctx, QWidget *parent = nullptr); explicit PageWalletFile(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr);
void initializePage() override; void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
bool isComplete() const override;
signals: signals:
void createWallet();
void defaultWalletDirChanged(QString walletDir); void defaultWalletDirChanged(QString walletDir);
private: private:
AppContext *m_ctx; QString defaultWalletName();
Ui::CreateWalletPage *ui; bool walletPathExists(const QString &walletName);
QString m_walletDir;
bool validateWidgets(); bool validateWidgets();
AppContext *m_ctx;
Ui::PageWalletFile *ui;
WizardFields *m_fields;
bool m_validated;
}; };
#endif //FEATHER_CREATEWALLET_H #endif //FEATHER_CREATEWALLET_H

View file

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageWalletFile</class>
<widget class="QWizardPage" name="PageWalletFile">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>486</width>
<height>317</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Wallet</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="line_walletDir">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnChange">
<property name="text">
<string>Change</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="line_walletName"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Directory</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="lockIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Choose a name and directory for your wallet files.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
<zorder>label_9</zorder>
<zorder>label</zorder>
<zorder>verticalSpacer</zorder>
<zorder>line_walletName</zorder>
<zorder>frame</zorder>
</widget>
<tabstops>
<tabstop>line_walletName</tabstop>
<tabstop>line_walletDir</tabstop>
<tabstop>btnChange</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -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 <QPlainTextEdit>
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;
}

View file

@ -13,23 +13,25 @@
#include "appcontext.h" #include "appcontext.h"
namespace Ui { namespace Ui {
class ViewOnlyPage; class PageWalletRestoreKeys;
} }
class ViewOnlyPage : public QWizardPage class PageWalletRestoreKeys : public QWizardPage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ViewOnlyPage(AppContext *ctx, QWidget *parent = nullptr); explicit PageWalletRestoreKeys(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr);
void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
void cleanupPage() const;
private: private:
void resetWidgets();
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel; WizardFields *m_fields;
Ui::ViewOnlyPage *ui; Ui::PageWalletRestoreKeys *ui;
}; };
#endif #endif

View file

@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageWalletRestoreKeys</class>
<widget class="QWizardPage" name="PageWalletRestoreKeys">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>759</width>
<height>460</height>
</rect>
</property>
<property name="windowTitle">
<string>ViewOnlyPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_address">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>To restore a view-only wallet leave the spend key blank.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Primary address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_address"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_viewKey">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Secret view key</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_viewkey"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_spendKey">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Secret spend key (optional)</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_spendkey"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="restoreFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_errorString">
<property name="text">
<string>errorString</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -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 <QLineEdit>
#include <QPlainTextEdit>
#include <QMessageBox>
#include <monero_seed/wordlist.hpp> // 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<QAbstractButton *>::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;
}

View file

@ -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 <QLabel>
#include <QWizardPage>
#include <QWidget>
#include <QTextEdit>
#include <QCompleter>
#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

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageWalletRestoreSeed</class>
<widget class="QWizardPage" name="PageWalletRestoreSeed">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>874</width>
<height>639</height>
</rect>
</property>
<property name="windowTitle">
<string>RestorePage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Select seed type:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QRadioButton" name="radio14">
<property name="text">
<string>14 word mnemonic seed</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">seedBtnGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radio25">
<property name="text">
<string>25 word mnemonic seed</string>
</property>
<attribute name="buttonGroup">
<string notr="true">seedBtnGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="TextEdit" name="seedEdit">
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Seed offset passphrase:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_seedOffset">
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
<property name="placeholderText">
<string>Leave blank if in doubt</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_errorString">
<property name="text">
<string>errorString</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>TextEdit</class>
<extends>QTextEdit</extends>
<header>utils/textedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="seedBtnGroup"/>
</buttongroups>
</ui>

View file

@ -1,24 +1,23 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project. // Copyright (c) 2020-2021, The Monero Project.
#include "wizard/createwalletseed.h" #include "WalletWizard.h"
#include "wizard/walletwizard.h" #include "PageWalletSeed.h"
#include "ui_createwalletseed.h" #include "ui_PageWalletSeed.h"
#include "globals.h"
#include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
CreateWalletSeedPage::CreateWalletSeedPage(AppContext *ctx, QWidget *parent) : PageWalletSeed::PageWalletSeed(AppContext *ctx, WizardFields *fields, QWidget *parent)
QWizardPage(parent), : QWizardPage(parent)
m_ctx(ctx), , m_ctx(ctx)
ui(new Ui::CreateWalletSeedPage) { , ui(new Ui::PageWalletSeed)
, m_fields(fields)
{
ui->setupUi(this); ui->setupUi(this);
this->setFinalPage(true);
this->setTitle("Wallet seed");
// hide ui element, we only need it for registerField QPixmap pixmap = QPixmap(":/assets/images/seed.png");
this->registerField("mnemonicSeed", ui->hiddenMnemonicSeed); ui->seedIcon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
ui->hiddenMnemonicSeed->hide();
ui->seedWord2->setHelpText("In addition to the private spend key, Tevador's 14 word seed scheme also encodes the " 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. " "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]{ connect(ui->btnCopy, &QPushButton::clicked, [this]{
Utils::copyToClipboard(m_mnemonic); Utils::copyToClipboard(m_mnemonic);
}); });
this->setButtonText(QWizard::FinishButton, "Create/Open wallet");
} }
void CreateWalletSeedPage::initializePage() { void PageWalletSeed::initializePage() {
this->generateSeed(); this->generateSeed();
this->setTitle(m_fields->modeText);
} }
void CreateWalletSeedPage::seedRoulette(int count) { void PageWalletSeed::seedRoulette(int count) {
count += 1; count += 1;
if (count > m_rouletteSpin) if (count > m_rouletteSpin)
return; return;
@ -50,14 +48,18 @@ void CreateWalletSeedPage::seedRoulette(int count) {
}); });
} }
void CreateWalletSeedPage::generateSeed() { void PageWalletSeed::generateSeed() {
FeatherSeed seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType], m_ctx->coinName, m_ctx->seedLanguage); do {
FeatherSeed seed = FeatherSeed(m_ctx->restoreHeights[m_ctx->networkType],
QString::fromStdString(globals::coinName), m_ctx->seedLanguage);
m_mnemonic = seed.mnemonic.join(" "); m_mnemonic = seed.mnemonic.join(" ");
m_restoreHeight = seed.restoreHeight; m_restoreHeight = seed.restoreHeight;
} while (m_mnemonic.split(" ").length() != 14); // https://github.com/tevador/monero-seed/issues/2
this->displaySeed(m_mnemonic); this->displaySeed(m_mnemonic);
} }
void CreateWalletSeedPage::displaySeed(const QString &seed){ void PageWalletSeed::displaySeed(const QString &seed){
QStringList seedSplit = seed.split(" "); QStringList seedSplit = seed.split(" ");
ui->seedWord1->setText(seedSplit[0]); ui->seedWord1->setText(seedSplit[0]);
@ -76,13 +78,13 @@ void CreateWalletSeedPage::displaySeed(const QString &seed){
ui->seedWord14->setText(seedSplit[13]); ui->seedWord14->setText(seedSplit[13]);
} }
int CreateWalletSeedPage::nextId() const { int PageWalletSeed::nextId() const {
return -1; return WalletWizard::Page_WalletFile;
} }
bool CreateWalletSeedPage::validatePage() { bool PageWalletSeed::validatePage() {
if(m_mnemonic.isEmpty()) return false; if (m_mnemonic.isEmpty()) return false;
if(!m_restoreHeight) return false; if (!m_restoreHeight) return false;
QMessageBox seedWarning(this); QMessageBox seedWarning(this);
seedWarning.setWindowTitle("Warning!"); seedWarning.setWindowTitle("Warning!");
@ -90,11 +92,15 @@ bool CreateWalletSeedPage::validatePage() {
"• Never type it on a website\n" "• Never type it on a website\n"
"• Store it safely (offline)\n" "• Store it safely (offline)\n"
"• Do not lose your seed!"); "• Do not lose your seed!");
seedWarning.addButton("Go back", QMessageBox::RejectRole);
seedWarning.addButton("I understand", QMessageBox::AcceptRole); 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; return true;
} }

View file

@ -12,15 +12,15 @@
#include "appcontext.h" #include "appcontext.h"
namespace Ui { namespace Ui {
class CreateWalletSeedPage; class PageWalletSeed;
} }
class CreateWalletSeedPage : public QWizardPage class PageWalletSeed : public QWizardPage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CreateWalletSeedPage(AppContext *ctx, QWidget *parent = nullptr); explicit PageWalletSeed(AppContext *ctx, WizardFields *fields, QWidget *parent = nullptr);
void initializePage() override; void initializePage() override;
bool validatePage() override; bool validatePage() override;
int nextId() const override; int nextId() const override;
@ -37,8 +37,9 @@ signals:
private: private:
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel; Ui::PageWalletSeed *ui;
Ui::CreateWalletSeedPage *ui;
WizardFields *m_fields;
QString m_mnemonic; QString m_mnemonic;
int m_restoreHeight; int m_restoreHeight;

View file

@ -0,0 +1,714 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageWalletSeed</class>
<widget class="QWizardPage" name="PageWalletSeed">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>877</width>
<height>408</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QLabel" name="seedIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Store your 14-word wallet seed in a safe location.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>This seed will allow you to recover your wallet in case of computer failure.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>1.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord1">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_13">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_27">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>4.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord4">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_11">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_19">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>7.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord7">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_8">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_13">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>10.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord10">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_4">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_16">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_31">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>13.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord13">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QFrame" name="frame_6">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>2.</string>
</property>
</widget>
</item>
<item>
<widget class="HelpLabel" name="seedWord2">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>5.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord5">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_14">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_25">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>8.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord8">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_12">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_21">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>11.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord11">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_9">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_15">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>14.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord14">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QFrame" name="frame_10">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_17">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>3.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord3">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_7">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>6.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord6">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_5">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_29">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>9.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord9">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_15">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_14">
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_23">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>12.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seedWord12">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCopy">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRoulette">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>HelpLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -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 <QLineEdit>
#include <QVBoxLayout>
#include <QScreen>
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);
}

69
src/wizard/WalletWizard.h Normal file
View file

@ -0,0 +1,69 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#ifndef WALLETWIZARD_H
#define WALLETWIZARD_H
#include <QWizard>
#include <QLabel>
#include <QRadioButton>
#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

View file

@ -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 <QFileDialog>
#include <QMessageBox>
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;
}

View file

@ -1,113 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateWalletPage</class>
<widget class="QWizardPage" name="CreateWalletPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>321</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Wallet</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLineEdit" name="password">
<property name="text">
<string/>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="9" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QLineEdit" name="directory"/>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="walletPath"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Directory</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Password (Optional)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="walletName"/>
</item>
<item row="7" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnChange">
<property name="text">
<string>Change directory</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<zorder>label_3</zorder>
<zorder>label_9</zorder>
<zorder>password</zorder>
<zorder>directory</zorder>
<zorder>label</zorder>
<zorder>verticalSpacer</zorder>
<zorder>walletName</zorder>
<zorder>walletPath</zorder>
<zorder>horizontalLayoutWidget</zorder>
</widget>
<tabstops>
<tabstop>walletName</tabstop>
<tabstop>walletPath</tabstop>
<tabstop>password</tabstop>
<tabstop>directory</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -1,530 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateWalletSeedPage</class>
<widget class="QWizardPage" name="CreateWalletSeedPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>529</width>
<height>510</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>1</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord1">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>5</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord5">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="title">
<string>9</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_16">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord9">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_13">
<property name="title">
<string>13</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord13">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>2</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="HelpLabel" name="seedWord2">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>6</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord6">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="title">
<string>10</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord10">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_14">
<property name="title">
<string>14</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_20">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord14">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>3</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord3">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>7</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord7">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>11</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_14">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord11">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>4</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord4">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_8">
<property name="title">
<string>8</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord8">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_12">
<property name="title">
<string>12</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="seedWord12">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCopy">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRoulette">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLineEdit" name="hiddenMnemonicSeed">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Please save these 14 words on paper. This seed will allow you to recover your wallet in case of computer failure.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>HelpLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -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 <QFileDialog>
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;
}

View file

@ -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 <QLineEdit>
#include <QPlainTextEdit>
#include <QMessageBox>
#include <monero_seed/wordlist.hpp> // 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<QAbstractButton *>::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;
}

View file

@ -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 <QLabel>
#include <QWizardPage>
#include <QWidget>
#include <QTextEdit>
#include <QCompleter>
#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

View file

@ -1,172 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RestorePage</class>
<widget class="QWizardPage" name="RestorePage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>502</width>
<height>506</height>
</rect>
</property>
<property name="windowTitle">
<string>RestorePage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QRadioButton" name="radio14">
<property name="text">
<string>14 word mnemonic seed</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">seedBtnGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radio25">
<property name="text">
<string>25 word mnemonic seed</string>
</property>
<attribute name="buttonGroup">
<string notr="true">seedBtnGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QFrame" name="seedFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>120</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="TextEdit" name="seedEdit">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="restoreFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="RestoreHeightWidget" name="restoreHeightWidget" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_restoreHeightInfo">
<property name="text">
<string>You may specify the &quot;restore height&quot;. 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.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_errorString">
<property name="text">
<string>errorString</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RestoreHeightWidget</class>
<extends>QWidget</extends>
<header>widgets/restoreheightwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TextEdit</class>
<extends>QTextEdit</extends>
<header>utils/textedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="seedBtnGroup"/>
</buttongroups>
</ui>

View file

@ -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 <QPlainTextEdit>
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;
}

View file

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ViewOnlyPage</class>
<widget class="QWizardPage" name="ViewOnlyPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>502</width>
<height>506</height>
</rect>
</property>
<property name="windowTitle">
<string>ViewOnlyPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Standard address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_address"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Secret view key</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_viewkey"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Secret spend key (optional)</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_spendkey"/>
</item>
<item>
<widget class="QFrame" name="restoreFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="RestoreHeightWidget" name="restoreHeightWidget" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_restoreHeightInfo">
<property name="text">
<string>You may specify the &quot;restore height&quot;. 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.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_errorString">
<property name="text">
<string>errorString</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RestoreHeightWidget</class>
<extends>QWidget</extends>
<header>widgets/restoreheightwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -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 <QLineEdit>
#include <QVBoxLayout>
#include <QScreen>
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);
}

View file

@ -1,35 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#ifndef WALLETWIZARD_H
#define WALLETWIZARD_H
#include <QWizard>
#include <QLabel>
#include <QRadioButton>
#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