mirror of
https://github.com/feather-wallet/feather.git
synced 2024-11-16 17:27:38 +00:00
Seed Recovery Dialog
This commit is contained in:
parent
3aae9ddcda
commit
0c4fbcfe5a
4 changed files with 764 additions and 0 deletions
269
src/dialog/SeedRecoveryDialog.cpp
Normal file
269
src/dialog/SeedRecoveryDialog.cpp
Normal file
|
@ -0,0 +1,269 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||
|
||||
#include "SeedRecoveryDialog.h"
|
||||
#include "ui_SeedRecoveryDialog.h"
|
||||
|
||||
#include <monero_seed/wordlist.hpp>
|
||||
#include "ColorScheme.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "polyseed/polyseed.h"
|
||||
#include "utils/AsyncTask.h"
|
||||
#include "device/device_default.hpp"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
|
||||
SeedRecoveryDialog::SeedRecoveryDialog(QWidget *parent)
|
||||
: WindowModalDialog(parent)
|
||||
, m_scheduler(this)
|
||||
, m_watcher(this)
|
||||
, ui(new Ui::SeedRecoveryDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
for (int i = 0; i != 2048; i++) {
|
||||
m_wordList << QString::fromStdString(wordlist::english.get_word(i));
|
||||
}
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Apply)->setText("Check");
|
||||
|
||||
connect(this, &SeedRecoveryDialog::progressUpdated, this, &SeedRecoveryDialog::onProgressUpdated);
|
||||
|
||||
disconnect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &SeedRecoveryDialog::checkSeed);
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]{
|
||||
m_cancelled = true;
|
||||
});
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]{
|
||||
m_cancelled = true;
|
||||
m_watcher.waitForFinished();
|
||||
this->close();
|
||||
});
|
||||
|
||||
connect(this, &SeedRecoveryDialog::searchFinished, this, &SeedRecoveryDialog::onFinished);
|
||||
connect(this, &SeedRecoveryDialog::matchFound, this, &SeedRecoveryDialog::onMatchFound);
|
||||
connect(this, &SeedRecoveryDialog::addressMatchFound, this, &SeedRecoveryDialog::onAddressMatchFound);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void SeedRecoveryDialog::onMatchFound(const QString &match) {
|
||||
ui->potentialSeeds->appendPlainText(match);
|
||||
}
|
||||
|
||||
void SeedRecoveryDialog::onAddressMatchFound(const QString &match) {
|
||||
ui->potentialSeeds->appendPlainText(QString("\nFound seed containing address:\n%1").arg(match));
|
||||
}
|
||||
|
||||
void SeedRecoveryDialog::onFinished(bool cancelled) {
|
||||
if (!cancelled) {
|
||||
ui->progressBar->setMaximum(100);
|
||||
ui->progressBar->setValue(100);
|
||||
}
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
|
||||
}
|
||||
|
||||
QStringList SeedRecoveryDialog::wordsWithRegex(const QRegularExpression ®ex) {
|
||||
return m_wordList.filter(regex);
|
||||
}
|
||||
|
||||
bool SeedRecoveryDialog::findNext(const QList<QStringList> &words, QList<int> &index) {
|
||||
if (words.length() != index.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (words.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = words.length() - 1; i >= 0; i--) {
|
||||
if ((words[i].length() - 1) > index[i]) {
|
||||
index[i] += 1;
|
||||
for (int j = i + 1; j < words.length(); j++) {
|
||||
index[j] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString SeedRecoveryDialog::mnemonic(const QList<QStringList> &words, const QList<int> &index) {
|
||||
if (words.length() != index.length()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QStringList mnemonic;
|
||||
for (int i = 0; i < words.length(); i++) {
|
||||
mnemonic.push_back(words[i][index[i]]);
|
||||
}
|
||||
|
||||
return mnemonic.join(" ");
|
||||
}
|
||||
|
||||
bool SeedRecoveryDialog::isAlpha(const QString &word) {
|
||||
for (const QChar &ch : word) {
|
||||
if (!ch.isLetter()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SeedRecoveryDialog::onProgressUpdated(int value) {
|
||||
ui->progressBar->setValue(value);
|
||||
}
|
||||
|
||||
void SeedRecoveryDialog::checkSeed() {
|
||||
m_cancelled = false;
|
||||
|
||||
ui->progressBar->setValue(0);
|
||||
ui->potentialSeeds->clear();
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true);
|
||||
|
||||
// Check address
|
||||
QString address = ui->line_address->text();
|
||||
crypto::public_key spkey = crypto::null_pkey;
|
||||
|
||||
if (!address.isEmpty()) {
|
||||
cryptonote::address_parse_info info;
|
||||
bool addressValid = cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, address.toStdString());
|
||||
if (!addressValid) {
|
||||
Utils::showError(this, "Invalid address entered");
|
||||
this->onFinished(false);
|
||||
return;
|
||||
}
|
||||
spkey = info.address.m_spend_public_key;
|
||||
}
|
||||
|
||||
QList<QLineEdit*> lineEdits = ui->group_seed->findChildren<QLineEdit*>();
|
||||
std::sort(lineEdits.begin(), lineEdits.end(), [](QLineEdit* a, QLineEdit* b) {
|
||||
return a->objectName() < b->objectName();
|
||||
});
|
||||
|
||||
QList<QStringList> words;
|
||||
uint64_t combinations = 1;
|
||||
|
||||
for (QLineEdit *lineEdit : lineEdits) {
|
||||
lineEdit->setStyleSheet("");
|
||||
}
|
||||
|
||||
for (QLineEdit *lineEdit : lineEdits) {
|
||||
ColorScheme::updateFromWidget(this);
|
||||
QString word = lineEdit->text();
|
||||
|
||||
QString wordRe = word;
|
||||
if (this->isAlpha(word)) {
|
||||
wordRe = QString("^%1").arg(wordRe);
|
||||
}
|
||||
QRegularExpression regex{wordRe};
|
||||
|
||||
if (!regex.isValid()) {
|
||||
lineEdit->setStyleSheet(ColorScheme::RED.asStylesheet(true));
|
||||
Utils::showError(this, "Invalid regex entered", QString("'%1' is not a valid regular expression").arg(wordRe));
|
||||
this->onFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList possibleWords = wordsWithRegex(regex);
|
||||
int numWords = possibleWords.length();
|
||||
|
||||
if (numWords == 1) {
|
||||
lineEdit->setStyleSheet(ColorScheme::GREEN.asStylesheet(true));
|
||||
}
|
||||
else if (numWords == 0) {
|
||||
lineEdit->setStyleSheet(ColorScheme::RED.asStylesheet(true));
|
||||
Utils::showError(this, "Word is not in wordlist", QString("No words found for: '%1'").arg(word));
|
||||
this->onFinished(false);
|
||||
return;
|
||||
} else {
|
||||
lineEdit->setStyleSheet(ColorScheme::YELLOW.asStylesheet(true));
|
||||
ui->potentialSeeds->appendPlainText(QString("Possible words for '%1': %2").arg(word, possibleWords.join(", ")));
|
||||
|
||||
if (combinations < std::numeric_limits<uint64_t>::max() / numWords) {
|
||||
combinations *= possibleWords.length();
|
||||
} else {
|
||||
Utils::showError(this, "Too many possible seeds", "Recovery infeasible");
|
||||
this->onFinished(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
words << possibleWords;
|
||||
}
|
||||
|
||||
if (spkey == crypto::null_pkey) {
|
||||
ui->potentialSeeds->appendPlainText("\nPossible seeds:");
|
||||
}
|
||||
|
||||
qDebug() << "Number of possible combinations: " << combinations;
|
||||
|
||||
ui->progressBar->setMaximum(combinations / 1000);
|
||||
|
||||
uint32_t major = ui->line_majorLookahead->text().toInt();
|
||||
uint32_t minor = ui->line_minorLookahead->text().toInt();
|
||||
|
||||
// Single threaded for now
|
||||
const auto future = m_scheduler.run([this, words, spkey, major, minor]{
|
||||
QList<int> index(16, 0);
|
||||
|
||||
qint64 i = 0;
|
||||
|
||||
do {
|
||||
if (m_cancelled) {
|
||||
emit searchFinished(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (++i % 1000 == 0) {
|
||||
emit progressUpdated(i / 1000);
|
||||
}
|
||||
|
||||
QString seedString = mnemonic(words, index);
|
||||
|
||||
crypto::secret_key key;
|
||||
try {
|
||||
polyseed::data seed(POLYSEED_MONERO);
|
||||
seed.decode(seedString.toStdString().c_str());
|
||||
seed.keygen(&key.data, sizeof(key.data));
|
||||
}
|
||||
catch (const polyseed::error& ex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle case where we don't know an address
|
||||
if (spkey == crypto::null_pkey) {
|
||||
emit matchFound(seedString);
|
||||
continue;
|
||||
}
|
||||
|
||||
cryptonote::account_base base;
|
||||
base.generate(key, true, false);
|
||||
|
||||
hw::device &hwdev = base.get_device();
|
||||
|
||||
for (int x = 0; x < major; x++) {
|
||||
const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(base.get_keys(), x, 0, minor);
|
||||
for (const auto &k : pkeys) {
|
||||
if (k == spkey) {
|
||||
emit addressMatchFound(seedString);
|
||||
emit searchFinished(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (findNext(words, index));
|
||||
|
||||
emit searchFinished(false);
|
||||
});
|
||||
|
||||
m_watcher.setFuture(future.second);
|
||||
}
|
||||
|
||||
SeedRecoveryDialog::~SeedRecoveryDialog() = default;
|
51
src/dialog/SeedRecoveryDialog.h
Normal file
51
src/dialog/SeedRecoveryDialog.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||
|
||||
#ifndef FEATHER_SEEDRECOVERYDIALOG_H
|
||||
#define FEATHER_SEEDRECOVERYDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "components.h"
|
||||
#include "utils/scheduler.h"
|
||||
|
||||
namespace Ui {
|
||||
class SeedRecoveryDialog;
|
||||
}
|
||||
|
||||
class SeedRecoveryDialog : public WindowModalDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SeedRecoveryDialog(QWidget *parent = nullptr);
|
||||
~SeedRecoveryDialog() override;
|
||||
|
||||
signals:
|
||||
void progressUpdated(int value);
|
||||
void searchFinished(bool cancelled);
|
||||
void matchFound(QString match);
|
||||
void addressMatchFound(QString match);
|
||||
|
||||
private slots:
|
||||
void onFinished(bool cancelled);
|
||||
void onMatchFound(const QString &match);
|
||||
void onAddressMatchFound(const QString &match);
|
||||
void onProgressUpdated(int value);
|
||||
|
||||
private:
|
||||
void checkSeed();
|
||||
QStringList wordsWithRegex(const QRegularExpression ®ex);
|
||||
bool isAlpha(const QString &word);
|
||||
bool findNext(const QList<QStringList> &words, QList<int> &index);
|
||||
QString mnemonic(const QList<QStringList> &words, const QList<int> &index);
|
||||
|
||||
std::atomic<bool> m_cancelled = false;
|
||||
|
||||
QStringList m_wordList;
|
||||
QFutureWatcher<void> m_watcher;
|
||||
FutureScheduler m_scheduler;
|
||||
QScopedPointer<Ui::SeedRecoveryDialog> ui;
|
||||
};
|
||||
|
||||
#endif //FEATHER_SEEDRECOVERYDIALOG_H
|
436
src/dialog/SeedRecoveryDialog.ui
Normal file
436
src/dialog/SeedRecoveryDialog.ui
Normal file
|
@ -0,0 +1,436 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SeedRecoveryDialog</class>
|
||||
<widget class="QDialog" name="SeedRecoveryDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>570</width>
|
||||
<height>632</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Seed Recovery</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>This tool allows you to recover partial Polyseeds.</p><p>Enter every seed word you know. If you know a word partially, fill in the part you know. If you don't know a word at all, leave it blank.</p><p>Regex is supported. Entries containing no special characters are assumed to be prefixes.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="group_seed">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="3" column="5">
|
||||
<widget class="QLineEdit" name="word_15">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>14.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="6">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>12.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>1.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="6">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>16.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="5">
|
||||
<widget class="QLineEdit" name="word_11">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>13.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>8.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>5.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="7">
|
||||
<widget class="QLineEdit" name="word_16">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="7">
|
||||
<widget class="QLineEdit" name="word_08">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLineEdit" name="word_02">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="word_13">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>2.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>10.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLineEdit" name="word_14">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>15.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<widget class="QLineEdit" name="word_07">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="word_09">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>11.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QLineEdit" name="word_03">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>9.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLineEdit" name="word_06">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>6.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="7">
|
||||
<widget class="QLineEdit" name="word_04">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>7.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="7">
|
||||
<widget class="QLineEdit" name="word_12">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>4.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="word_01">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="word_05">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>3.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLineEdit" name="word_10">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="text">
|
||||
<string>Enter any deposit address associated with the wallet (optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="line_address"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>Account lookahead</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="line_majorLookahead">
|
||||
<property name="text">
|
||||
<string>50</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>50</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
<string>Address lookahead</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="line_minorLookahead">
|
||||
<property name="text">
|
||||
<string>200</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>200</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frameProgress">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</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="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Progress</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="potentialSeeds">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>word_01</tabstop>
|
||||
<tabstop>word_02</tabstop>
|
||||
<tabstop>word_03</tabstop>
|
||||
<tabstop>word_04</tabstop>
|
||||
<tabstop>word_05</tabstop>
|
||||
<tabstop>word_06</tabstop>
|
||||
<tabstop>word_07</tabstop>
|
||||
<tabstop>word_08</tabstop>
|
||||
<tabstop>word_09</tabstop>
|
||||
<tabstop>word_10</tabstop>
|
||||
<tabstop>word_11</tabstop>
|
||||
<tabstop>word_12</tabstop>
|
||||
<tabstop>word_13</tabstop>
|
||||
<tabstop>word_14</tabstop>
|
||||
<tabstop>word_15</tabstop>
|
||||
<tabstop>word_16</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SeedRecoveryDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SeedRecoveryDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -9,7 +9,9 @@
|
|||
#include <QDialogButtonBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "dialog/SeedRecoveryDialog.h"
|
||||
#include <monero_seed/wordlist.hpp> // tevador 14 word
|
||||
#include "utils/Seed.h"
|
||||
#include "constants.h"
|
||||
|
@ -57,6 +59,12 @@ PageWalletRestoreSeed::PageWalletRestoreSeed(WizardFields *fields, QWidget *pare
|
|||
ui->seedEdit->setAcceptRichText(false);
|
||||
ui->seedEdit->setMaximumHeight(150);
|
||||
|
||||
QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+K"), this);
|
||||
QObject::connect(shortcut, &QShortcut::activated, [&](){
|
||||
SeedRecoveryDialog dialog{this};
|
||||
dialog.exec();
|
||||
});
|
||||
|
||||
connect(ui->seedBtnGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this, &PageWalletRestoreSeed::onSeedTypeToggled);
|
||||
connect(ui->combo_seedLanguage, &QComboBox::currentTextChanged, this, &PageWalletRestoreSeed::onSeedLanguageChanged);
|
||||
connect(ui->btnOptions, &QPushButton::clicked, this, &PageWalletRestoreSeed::onOptionsClicked);
|
||||
|
|
Loading…
Reference in a new issue