Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release-notes-6946.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GUI changes
--------

- A mnemonic verification dialog is now shown after creating a new HD wallet, requiring users to verify they have written down their recovery phrase (#6946).
- A new menu item "Show Recovery Phrase…" has been added to the Settings menu to view the recovery phrase for existing HD wallets (#6946).

4 changes: 4 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ QT_FORMS_UI = \
qt/forms/intro.ui \
qt/forms/modaloverlay.ui \
qt/forms/masternodelist.ui \
qt/forms/mnemonicverificationdialog.ui \
qt/forms/qrdialog.ui \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
Expand Down Expand Up @@ -66,6 +67,7 @@ QT_MOC_CPP = \
qt/moc_macnotificationhandler.cpp \
qt/moc_modaloverlay.cpp \
qt/moc_masternodelist.cpp \
qt/moc_mnemonicverificationdialog.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
qt/moc_optionsdialog.cpp \
Expand Down Expand Up @@ -144,6 +146,7 @@ BITCOIN_QT_H = \
qt/macos_appnap.h \
qt/modaloverlay.h \
qt/masternodelist.h \
qt/mnemonicverificationdialog.h \
qt/networkstyle.h \
qt/notificator.h \
qt/openuridialog.h \
Expand Down Expand Up @@ -257,6 +260,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/governancelist.cpp \
qt/proposalwizard.cpp \
qt/masternodelist.cpp \
qt/mnemonicverificationdialog.cpp \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
qt/paymentserver.cpp \
Expand Down
3 changes: 3 additions & 0 deletions src/interfaces/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ class Wallet
//! Return whether is a legacy wallet
virtual bool isLegacy() = 0;

//! Get mnemonic phrase from wallet.
virtual bool getMnemonic(SecureString& mnemonic_out, SecureString& mnemonic_passphrase_out) = 0;

//! Register handler for unload message.
using UnloadFn = std::function<void()>;
virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0;
Expand Down
6 changes: 6 additions & 0 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ void BitcoinGUI::createActions()
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
changePassphraseAction = new QAction(tr("&Change Passphrase…"), this);
changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
showMnemonicAction = new QAction(tr("&Show Recovery Phrase…"), this);
showMnemonicAction->setStatusTip(tr("Show the recovery phrase (mnemonic seed) for this wallet"));
unlockWalletAction = new QAction(tr("&Unlock Wallet…"), this);
unlockWalletAction->setToolTip(tr("Unlock wallet"));
lockWalletAction = new QAction(tr("&Lock Wallet"), this);
Expand Down Expand Up @@ -502,6 +504,7 @@ void BitcoinGUI::createActions()
connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet);
connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet);
connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
connect(showMnemonicAction, &QAction::triggered, walletFrame, &WalletFrame::showMnemonic);
connect(unlockWalletAction, &QAction::triggered, walletFrame, &WalletFrame::unlockWallet);
connect(lockWalletAction, &QAction::triggered, walletFrame, &WalletFrame::lockWallet);
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
Expand Down Expand Up @@ -620,6 +623,7 @@ void BitcoinGUI::createMenuBar()
{
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addAction(showMnemonicAction);
settings->addAction(unlockWalletAction);
settings->addAction(lockWalletAction);
settings->addSeparator();
Expand Down Expand Up @@ -1040,6 +1044,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
encryptWalletAction->setEnabled(enabled);
backupWalletAction->setEnabled(enabled);
changePassphraseAction->setEnabled(enabled);
showMnemonicAction->setEnabled(enabled);
unlockWalletAction->setEnabled(enabled);
lockWalletAction->setEnabled(enabled);
signMessageAction->setEnabled(enabled);
Expand Down Expand Up @@ -1936,6 +1941,7 @@ void BitcoinGUI::setEncryptionStatus(int status)
encryptWalletAction->setChecked(false);
changePassphraseAction->setEnabled(false);
encryptWalletAction->setEnabled(false);
showMnemonicAction->setEnabled(false);
break;
case WalletModel::Unencrypted:
labelWalletEncryptionIcon->show();
Expand Down
1 change: 1 addition & 0 deletions src/qt/bitcoingui.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class BitcoinGUI : public QMainWindow
QAction* encryptWalletAction = nullptr;
QAction* backupWalletAction = nullptr;
QAction* changePassphraseAction = nullptr;
QAction* showMnemonicAction = nullptr;
QAction* unlockWalletAction = nullptr;
QAction* lockWalletAction = nullptr;
QAction* aboutQtAction = nullptr;
Expand Down
12 changes: 12 additions & 0 deletions src/qt/createwalletdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <qt/guiutil.h>

#include <QPushButton>
#include <QToolButton>

CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
QDialog(parent, GUIUtil::dialog_flags),
Expand All @@ -22,6 +23,17 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
ui->wallet_name_line_edit->setFocus(Qt::ActiveWindowFocusReason);

// Hide advanced options by default and provide a compact toggle control.
ui->groupBox->setVisible(false);
ui->groupBox->setTitle(QString());
ui->advanced_toggle_button->setChecked(false);
ui->advanced_toggle_button->setArrowType(Qt::RightArrow);
ui->advanced_toggle_button->setFocusPolicy(Qt::NoFocus);
connect(ui->advanced_toggle_button, &QToolButton::toggled, this, [this](bool checked) {
ui->groupBox->setVisible(checked);
ui->advanced_toggle_button->setArrowType(checked ? Qt::DownArrow : Qt::RightArrow);
});

connect(ui->wallet_name_line_edit, &QLineEdit::textEdited, [this](const QString& text) {
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
});
Expand Down
48 changes: 47 additions & 1 deletion src/qt/forms/createwalletdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>8</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
Expand Down Expand Up @@ -71,11 +74,54 @@
</spacer>
</item>
<item>
<widget class="QToolButton" name="advanced_toggle_button">
<property name="text">
<string>Advanced Options</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="arrowType">
<enum>Qt::RightArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced Options</string>
<string/>
</property>
<property name="visible">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_groupbox">
<property name="topMargin">
<number>0</number>
</property>
<property name="leftMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QCheckBox" name="disable_privkeys_checkbox">
<property name="enabled">
Expand Down
206 changes: 206 additions & 0 deletions src/qt/forms/mnemonicverificationdialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MnemonicVerificationDialog</class>
<widget class="QDialog" name="MnemonicVerificationDialog">
<property name="windowTitle">
<string>Save Your Mnemonic</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="step1Page">
<layout class="QVBoxLayout" name="verticalLayout_step1">
<item>
<widget class="QLabel" name="warningLabel">
<property name="text">
<string>WARNING: If you lose your mnemonic seed phrase, you will lose access to your wallet forever.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="instructionLabel">
<property name="text">
<string>Please write down these words in order. You will need them to restore your wallet.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="mnemonicScroll">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="mnemonicGridWidget">
<layout class="QGridLayout" name="mnemonicGridLayout"/>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_buttons">
<item>
<widget class="QPushButton" name="showMnemonicButton">
<property name="text">
<string>Show</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="hideMnemonicButton">
<property name="text">
<string>Hide</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="writtenDownCheckbox">
<property name="text">
<string>I have written down my mnemonic</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="step2Page">
<layout class="QVBoxLayout" name="verticalLayout_step2">
<item>
<widget class="QLabel" name="verificationLabel">
<property name="text">
<string>To verify you've saved your mnemonic, please enter the following words:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="word1Label">
<property name="text">
<string>Word #1:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_word1">
<item>
<widget class="QLineEdit" name="word1Edit"/>
</item>
<item>
<widget class="QLabel" name="word1Status"/>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="word2Label">
<property name="text">
<string>Word #2:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_word2">
<item>
<widget class="QLineEdit" name="word2Edit"/>
</item>
<item>
<widget class="QLabel" name="word2Status"/>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="word3Label">
<property name="text">
<string>Word #3:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_word3">
<item>
<widget class="QLineEdit" name="word3Edit"/>
</item>
<item>
<widget class="QLabel" name="word3Status"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_actions">
<item>
<widget class="QPushButton" name="showMnemonicAgainButton">
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>MnemonicVerificationDialog</receiver>
<slot>accept()</slot>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>MnemonicVerificationDialog</receiver>
<slot>reject()</slot>
</connection>
</connections>
</ui>

Loading
Loading