diff --git a/contrib/bitcoin-qt.pro b/contrib/bitcoin-qt.pro
--- a/contrib/bitcoin-qt.pro
+++ b/contrib/bitcoin-qt.pro
@@ -4,6 +4,7 @@
../src/qt/forms/askpassphrasedialog.ui \
../src/qt/forms/coincontroldialog.ui \
../src/qt/forms/editaddressdialog.ui \
+ ../src/qt/forms/encryptwalletadvanceddialog.ui \
../src/qt/forms/helpmessagedialog.ui \
../src/qt/forms/intro.ui \
../src/qt/forms/openuridialog.ui \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -99,6 +99,7 @@
qt/forms/askpassphrasedialog.ui \
qt/forms/coincontroldialog.ui \
qt/forms/editaddressdialog.ui \
+ qt/forms/encryptwalletadvanceddialog.ui \
qt/forms/helpmessagedialog.ui \
qt/forms/intro.ui \
qt/forms/modaloverlay.ui \
@@ -127,6 +128,7 @@
qt/moc_coincontroltreewidget.cpp \
qt/moc_csvmodelwriter.cpp \
qt/moc_editaddressdialog.cpp \
+ qt/moc_encryptwalletadvanceddialog.cpp \
qt/moc_guiutil.cpp \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
@@ -194,6 +196,7 @@
qt/coincontroltreewidget.h \
qt/csvmodelwriter.h \
qt/editaddressdialog.h \
+ qt/encryptwalletadvanceddialog.h \
qt/guiconstants.h \
qt/guiutil.h \
qt/intro.h \
@@ -324,6 +327,7 @@
qt/coincontroldialog.cpp \
qt/coincontroltreewidget.cpp \
qt/editaddressdialog.cpp \
+ qt/encryptwalletadvanceddialog.cpp \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
qt/paymentrequestplus.cpp \
diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt
--- a/src/qt/CMakeLists.txt
+++ b/src/qt/CMakeLists.txt
@@ -37,6 +37,7 @@
forms/askpassphrasedialog.ui
forms/coincontroldialog.ui
forms/editaddressdialog.ui
+ forms/encryptwalletadvanceddialog.ui
forms/helpmessagedialog.ui
forms/intro.ui
forms/modaloverlay.ui
@@ -132,6 +133,7 @@
coincontroldialog.cpp
coincontroltreewidget.cpp
editaddressdialog.cpp
+ encryptwalletadvanceddialog.cpp
openuridialog.cpp
overviewpage.cpp
paymentrequestplus.cpp
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -112,6 +112,7 @@
QAction *optionsAction;
QAction *toggleHideAction;
QAction *encryptWalletAction;
+ QAction *encryptWalletAdvancedAction;
QAction *backupWalletAction;
QAction *changePassphraseAction;
QAction *aboutQtAction;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -91,11 +91,12 @@
usedReceivingAddressesAction(0), signMessageAction(0),
verifyMessageAction(0), aboutAction(0), receiveCoinsAction(0),
receiveCoinsMenuAction(0), optionsAction(0), toggleHideAction(0),
- encryptWalletAction(0), backupWalletAction(0), changePassphraseAction(0),
- aboutQtAction(0), openRPCConsoleAction(0), openAction(0),
- showHelpMessageAction(0), trayIcon(0), trayIconMenu(0), notificator(0),
- rpcConsole(0), helpMessageDialog(0), modalOverlay(0), prevBlocks(0),
- spinnerFrame(0), platformStyle(_platformStyle), cfg(cfg) {
+ encryptWalletAction(0), encryptWalletAdvancedAction(0),
+ backupWalletAction(0), changePassphraseAction(0), aboutQtAction(0),
+ openRPCConsoleAction(0), openAction(0), showHelpMessageAction(0),
+ trayIcon(0), trayIconMenu(0), notificator(0), rpcConsole(0),
+ helpMessageDialog(0), modalOverlay(0), prevBlocks(0), spinnerFrame(0),
+ platformStyle(_platformStyle), cfg(cfg) {
GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
QString windowTitle = tr(PACKAGE_NAME) + " - ";
@@ -368,6 +369,14 @@
encryptWalletAction->setStatusTip(
tr("Encrypt the private keys that belong to your wallet"));
encryptWalletAction->setCheckable(true);
+
+ encryptWalletAdvancedAction =
+ new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"),
+ tr("&Encrypt Wallet Advanced..."), this);
+ encryptWalletAdvancedAction->setStatusTip(
+ tr("Encrypt and Recover or Manually Generate your wallet's HD key"));
+ encryptWalletAdvancedAction->setCheckable(true);
+
backupWalletAction =
new QAction(platformStyle->TextColorIcon(":/icons/filesave"),
tr("&Backup Wallet..."), this);
@@ -439,6 +448,8 @@
if (walletFrame) {
connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame,
SLOT(encryptWallet(bool)));
+ connect(encryptWalletAdvancedAction, SIGNAL(triggered(bool)),
+ walletFrame, SLOT(encryptWalletAdvanced(bool)));
connect(backupWalletAction, SIGNAL(triggered()), walletFrame,
SLOT(backupWallet()));
connect(changePassphraseAction, SIGNAL(triggered()), walletFrame,
@@ -488,6 +499,7 @@
QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
if (walletFrame) {
settings->addAction(encryptWalletAction);
+ settings->addAction(encryptWalletAdvancedAction);
settings->addAction(changePassphraseAction);
settings->addSeparator();
}
@@ -613,6 +625,7 @@
receiveCoinsMenuAction->setEnabled(enabled);
historyAction->setEnabled(enabled);
encryptWalletAction->setEnabled(enabled);
+ encryptWalletAdvancedAction->setEnabled(enabled);
backupWalletAction->setEnabled(enabled);
changePassphraseAction->setEnabled(enabled);
signMessageAction->setEnabled(enabled);
@@ -1098,8 +1111,10 @@
case WalletModel::Unencrypted:
labelWalletEncryptionIcon->hide();
encryptWalletAction->setChecked(false);
+ encryptWalletAdvancedAction->setChecked(false);
changePassphraseAction->setEnabled(false);
encryptWalletAction->setEnabled(true);
+ encryptWalletAdvancedAction->setEnabled(true);
break;
case WalletModel::Unlocked:
labelWalletEncryptionIcon->show();
@@ -1109,9 +1124,12 @@
labelWalletEncryptionIcon->setToolTip(
tr("Wallet is encrypted and currently unlocked"));
encryptWalletAction->setChecked(true);
+ encryptWalletAdvancedAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(
false); // TODO: decrypt currently not supported
+ encryptWalletAdvancedAction->setEnabled(
+ false); // TODO: decrypt currently not supported
break;
case WalletModel::Locked:
labelWalletEncryptionIcon->show();
@@ -1121,9 +1139,12 @@
labelWalletEncryptionIcon->setToolTip(
tr("Wallet is encrypted and currently locked"));
encryptWalletAction->setChecked(true);
+ encryptWalletAdvancedAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(
false); // TODO: decrypt currently not supported
+ encryptWalletAdvancedAction->setEnabled(
+ false); // TODO: decrypt currently not supported
break;
}
}
diff --git a/src/qt/encryptwalletadvanceddialog.h b/src/qt/encryptwalletadvanceddialog.h
new file mode 100644
--- /dev/null
+++ b/src/qt/encryptwalletadvanceddialog.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_ENCRYPTWALLETADVANCEDDIALOG_H
+#define BITCOIN_QT_ENCRYPTWALLETADVANCEDDIALOG_H
+
+#include
+
+#include "base58.h"
+#include "support/allocators/secure.h"
+#include "util.h"
+
+class WalletModel;
+
+namespace Ui {
+class AskEncryptAdvancedDialog;
+}
+
+/** Multifunctional dialog to ask for passphrases. Used for encryption,
+ * unlocking, and changing the passphrase.
+ */
+class EncryptWalletAdvancedDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit EncryptWalletAdvancedDialog(QWidget *parent);
+ ~EncryptWalletAdvancedDialog();
+
+ void accept() override;
+
+ void setModel(WalletModel *model);
+
+private:
+ Ui::AskEncryptAdvancedDialog *ui;
+ WalletModel *model;
+ bool fCapsLock;
+ void DecodeDiceRolls(SecureString diceRolls, std::vector &retVec);
+ void SetKeyWithVector(std::vector vec, CKey &destKey);
+
+private Q_SLOTS:
+ void textChanged();
+ void secureClearPassFields();
+
+protected:
+ bool event(QEvent *event) override;
+ bool eventFilter(QObject *object, QEvent *event) override;
+};
+
+#endif // BITCOIN_QT_ENCRYPTWALLETADVANCEDDIALOG_H
diff --git a/src/qt/encryptwalletadvanceddialog.cpp b/src/qt/encryptwalletadvanceddialog.cpp
new file mode 100644
--- /dev/null
+++ b/src/qt/encryptwalletadvanceddialog.cpp
@@ -0,0 +1,402 @@
+// Copyright (c) 2011-2018 The Bitcoin ABC developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+#endif
+
+#include "encryptwalletadvanceddialog.h"
+#include "ui_encryptwalletadvanceddialog.h"
+
+#include "guiconstants.h"
+#include "walletmodel.h"
+
+#include "support/allocators/secure.h"
+
+#include "base58.h"
+#include "util.h"
+
+#include
+#include
+#include
+
+EncryptWalletAdvancedDialog::EncryptWalletAdvancedDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::AskEncryptAdvancedDialog), model(0),
+ fCapsLock(false) {
+
+ ui->setupUi(this);
+
+ ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
+ ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
+
+ ui->keyEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
+ ui->keyEdit2->setMaxLength(128);
+ ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
+ ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
+
+ // Setup Caps Lock detection.
+ ui->keyEdit1->installEventFilter(this);
+ ui->keyEdit2->installEventFilter(this);
+ ui->passEdit1->installEventFilter(this);
+ ui->passEdit2->installEventFilter(this);
+
+ textChanged();
+ connect(ui->keyEdit1, SIGNAL(textChanged(QString)), this,
+ SLOT(textChanged()));
+ connect(ui->keyEdit2, SIGNAL(textChanged(QString)), this,
+ SLOT(textChanged()));
+ connect(ui->passEdit1, SIGNAL(textChanged(QString)), this,
+ SLOT(textChanged()));
+ connect(ui->passEdit2, SIGNAL(textChanged(QString)), this,
+ SLOT(textChanged()));
+}
+
+EncryptWalletAdvancedDialog::~EncryptWalletAdvancedDialog() {
+ secureClearPassFields();
+ delete ui;
+}
+
+void EncryptWalletAdvancedDialog::setModel(WalletModel *_model) {
+ this->model = _model;
+}
+
+void EncryptWalletAdvancedDialog::accept() {
+
+ if (!model) return;
+
+ SecureString newkey1, newkey2, newpass1, newpass2;
+
+ newkey1.reserve(MAX_PASSPHRASE_SIZE);
+ newkey2.reserve(MAX_PASSPHRASE_SIZE);
+ newpass1.reserve(MAX_PASSPHRASE_SIZE);
+ newpass2.reserve(MAX_PASSPHRASE_SIZE);
+
+ // TODO: get rid of this .c_str() by implementing
+ // SecureString::operator=(std::string)
+ // Alternately, find a way to make this input mlock()'d to begin with.
+ newkey1.assign(ui->keyEdit1->text().toStdString().c_str());
+ newkey2.assign(ui->keyEdit2->text().toStdString().c_str());
+ newpass1.assign(ui->passEdit1->text().toStdString().c_str());
+ newpass2.assign(ui->passEdit2->text().toStdString().c_str());
+
+ // Validate the passphrases
+ if (newpass1 != newpass2 || newpass1.empty() || newpass2.empty()) {
+ QMessageBox::critical(this, tr("Wallet encryption failed"),
+ tr("The supplied passphrases do not match."));
+ return;
+ }
+
+ CKey newSeed;
+
+ if (ui->keyRadioButton1->isChecked()) {
+
+ // Option 1: Generate New Master Seed
+
+ // Leave newSeed as invalid, causes new seed to be generated
+ // in CWallet::GenerateNewHDMasterKey
+
+ } else if (ui->keyRadioButton2->isChecked()) {
+
+ // Option 2: Recover from backup HD Master Key
+
+ std::vector decodedVec;
+ bool successful = DecodeBase58(newkey1.c_str(), decodedVec);
+ if (!successful || decodedVec.size() != 32) {
+ QMessageBox::critical(
+ this, tr("Wallet encryption failed"),
+ tr("Invalid Master Seed entered. "
+ "Re-enter the Master Seed and try again. "));
+ return;
+ }
+
+ newSeed.Set(decodedVec.begin(), decodedVec.end(), true);
+ if (!newSeed.IsValid()) {
+ QMessageBox::critical(
+ this, tr("Wallet encryption failed"),
+ tr("Master Seed rejected as invalid. "
+ "Re-enter the Master Seed and try again. "));
+ return;
+ }
+
+ } else if (ui->keyRadioButton3->isChecked()) {
+
+ // Option 3: Create New Master Seed by rolling dice for entropy
+
+ if (newkey2.size() != 128) {
+ std::string numrolls = std::to_string(newkey2.size());
+ QMessageBox::critical(
+ this, tr("Wallet encryption failed"),
+ tr("Invalid number of dice rolls entered. "
+ "Requires 128 dice rolls but only %1 provided. "
+ "Re-enter the correct number and try again. ")
+ .arg(tr(numrolls.c_str())));
+ return;
+ }
+
+ if (strspn(newkey2.c_str(), "1234") != 128) {
+ QMessageBox::critical(
+ this, tr("Wallet encryption failed"),
+ tr("Invalid input entered. Dice rolls much be "
+ "entered as characters '1' to '4'. "
+ "Re-enter dice rolls correctly and try again. "));
+ return;
+ }
+
+ std::vector decodedVec;
+ DecodeDiceRolls(newkey2, decodedVec);
+ SetKeyWithVector(decodedVec, newSeed);
+
+ } else {
+
+ // No option provided, error and retry
+ QMessageBox::critical(this, tr("No option selected"),
+ tr("No Master Seed operation selected. "
+ "Select an operation to perform and retry."));
+ return;
+ }
+
+ secureClearPassFields();
+
+ QMessageBox::StandardButton retval = QMessageBox::question(
+ this, tr("Confirm wallet encryption"),
+ tr("Warning: If you encrypt your wallet and lose your "
+ "passphrase, you will LOSE ALL OF YOUR BITCOINS!") +
+ "
" + tr("Are you sure you wish to encrypt your wallet?"),
+ QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
+
+ if (retval == QMessageBox::Yes) {
+ if (newSeed.IsValid()) {
+ LogPrintf("EncryptWalletAdvancedDialog::accept - Sending user "
+ "inputed masterseed\n");
+ } else {
+ LogPrintf("EncryptWalletAdvancedDialog::accept - Requesting auto "
+ "generated masterseed \n");
+ }
+ if (model->setWalletEncrypted(true, newpass1, &newSeed)) {
+
+ if (ui->keyRadioButton1->isChecked() ||
+ ui->keyRadioButton3->isChecked()) {
+ // Messages for newly generated Master Seeds
+ std::string masterseed =
+ EncodeBase58(newSeed.begin(), newSeed.end());
+ QMessageBox::warning(
+ this, tr("Wallet encrypted"),
+ "" +
+ tr("%1 will close now to finish the encryption "
+ "process. "
+ "Remember, encrypting your wallet does not fully "
+ "protect "
+ "your bitcoins from being stolen by malware "
+ "infecting your computer.")
+ .arg(tr(PACKAGE_NAME)) +
+ "
" +
+ tr("IMPORTANT: Any previous backups made of your "
+ "wallet file must "
+ "be replaced with the newly encrypted wallet file. "
+ "For security, "
+ "prior backups of the unencrypted wallet file "
+ "cannot be used "
+ "once you start using the newly encrypted wallet.") +
+ "
" +
+ tr("The new Master Seed for this wallet is: "
+ "\"%1\"")
+ .arg(tr(masterseed.c_str())) +
+ "
" +
+ tr("Save this Master Seed to recover your wallet in "
+ "the event the "
+ "wallet file is lost. ") +
+ "
" +
+ tr("WARNING:"
+ "- Access to the Master Seed provides "
+ "COMPLETE ACCESS to ALL "
+ "the bitcoins in your wallet. You must SECURELY "
+ "STORE the Master Seed "
+ "to protect your wallet.
"
+ "- The Master Seed can only recover new "
+ "addresses. Addresses "
+ "already in your wallet cannot be recovered with "
+ "this Master Seed "
+ "and require the wallet file to use.
"
+ "- It is highly recommended to TEST the new "
+ "Master Seed above "
+ "by using it to recover an empty wallet file to "
+ "ensure the Master "
+ "Seed is stored correctly and works. Testing should "
+ "be done prior to "
+ "using this new wallet.
") +
+ "");
+ QApplication::quit();
+ } else {
+ // Messages for newly recovered from backup Master Seeds
+ std::string masterseed =
+ EncodeBase58(newSeed.begin(), newSeed.end());
+ QMessageBox::warning(
+ this, tr("Wallet encrypted"),
+ "" +
+ tr("%1 will close now to finish the encryption "
+ "process. "
+ "Remember, encrypting your wallet does not fully "
+ "protect "
+ "your bitcoins from being stolen by malware "
+ "infecting your computer.")
+ .arg(tr(PACKAGE_NAME)) +
+ "
" +
+ tr("IMPORTANT: Any previous backups made of your "
+ "wallet file must "
+ "be replaced with the newly encrypted wallet file. "
+ "For security, "
+ "prior backups of the unencrypted wallet file "
+ "cannot be used "
+ "once you start using the newly encrypted wallet.") +
+ "
" +
+ tr("The Master Seed for this wallet was restored to: "
+ "\"%1\"")
+ .arg(tr(masterseed.c_str())) +
+ "
" +
+ tr("To complete the recovery process it is necessary "
+ "to rebuild "
+ "the wallet's keypool and rescan prior blocks for "
+ "transactions "
+ "with the follwing steps: ") +
+ tr(""
+ "- Restart bitcoin-qt and from the console run "
+ "\"keypoolrefill "
+ "<number>\" to expand the wallet's keypool. "
+ "This process uses "
+ "the Master Seed to re-create addresses.
"
+ "- Quit and restart bitcoin-qt again with the "
+ "command line parameter "
+ "\"-rescan=1\". This forces previous blocks to be "
+ "rescanned for "
+ "new addresses added to the wallet in the prior "
+ "step. After rescanning is "
+ "complete the recovered wallet file should have "
+ "located and now "
+ "contain transactions and bitcoins from the lost "
+ "wallet.
") +
+ "
" +
+ tr("NOTE: This process requires the keypool to be "
+ "refilled "
+ "to contain at least the same number of addresses "
+ "as the "
+ "prior wallet file contained. You might need to "
+ "interate "
+ "and refill using a larger number of addresses "
+ "to recover all bitcoins from the lost wallet. ") +
+ "");
+ QApplication::quit();
+ }
+ } else {
+ QMessageBox::critical(
+ this, tr("Wallet encryption failed"),
+ tr("Wallet encryption failed due to an internal "
+ "error. Your wallet was not encrypted."));
+ }
+ QDialog::accept(); // Success
+ } else {
+ QDialog::reject(); // Cancelled
+ }
+}
+
+// Decode a sequence of 128 four-sided dice rolls into a 32-byte vector
+void EncryptWalletAdvancedDialog::DecodeDiceRolls(
+ SecureString diceRolls, std::vector &retVec) {
+
+ uint8_t currVal = 0, vecPos = 0, bytePos = 0;
+ std::string currRoll;
+
+ retVec.resize(32);
+
+ // Loop through each dice role, each roll provides 2 bits and 4 rolls
+ // provides 1 byte Once 1 byte is decoded, add it to the return vector
+ for (uint32_t i = 0; i < diceRolls.size(); i++) {
+ bytePos = i % 4;
+ currRoll = diceRolls[i];
+ currVal += ((std::stoul(currRoll) - 1) << (bytePos * 2));
+ if (bytePos == 3) {
+ retVec[vecPos++] = currVal;
+ currVal = 0;
+ }
+ }
+}
+
+// Assign a key the value of a random 32-byte vector. Due to requirements
+// some randomly generated vectors are not valid, if this happens increment
+// the vector until a valid value is found
+void EncryptWalletAdvancedDialog::SetKeyWithVector(std::vector vec,
+ CKey &destKey) {
+
+ uint8_t pos = 0, nextVal;
+
+ destKey.Set(vec.begin(), vec.end(), true);
+ while (!destKey.IsValid()) {
+ nextVal = vec[pos] == 255 ? 0 : vec[pos] + 1;
+ vec[pos++] = nextVal;
+ if (pos == 32) pos = 0;
+ destKey.Set(vec.begin(), vec.end(), true);
+ }
+}
+
+void EncryptWalletAdvancedDialog::textChanged() {
+ // Validate input, set Ok button to enabled when acceptable
+ bool acceptable = false;
+ acceptable =
+ !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty();
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
+}
+
+bool EncryptWalletAdvancedDialog::event(QEvent *event) {
+ // Detect Caps Lock key press.
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast(event);
+ if (ke->key() == Qt::Key_CapsLock) {
+ fCapsLock = !fCapsLock;
+ }
+ if (fCapsLock) {
+ ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
+ } else {
+ ui->capsLabel->clear();
+ }
+ }
+ return QWidget::event(event);
+}
+
+bool EncryptWalletAdvancedDialog::eventFilter(QObject *object, QEvent *event) {
+ /* Detect Caps Lock.
+ * There is no good OS-independent way to check a key state in Qt, but we
+ * can detect Caps Lock by checking for the following condition:
+ * Shift key is down and the result is a lower case character, or
+ * Shift key is not down and the result is an upper case character.
+ */
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast(event);
+ QString str = ke->text();
+ if (str.length() != 0) {
+ const QChar *psz = str.unicode();
+ bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
+ if ((fShift && *psz >= 'a' && *psz <= 'z') ||
+ (!fShift && *psz >= 'A' && *psz <= 'Z')) {
+ fCapsLock = true;
+ ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
+ } else if (psz->isLetter()) {
+ fCapsLock = false;
+ ui->capsLabel->clear();
+ }
+ }
+ }
+ return QDialog::eventFilter(object, event);
+}
+
+static void SecureClearQLineAdvancedEdit(QLineEdit *edit) {
+ // Attempt to overwrite text so that they do not linger around in memory
+ edit->setText(QString(" ").repeated(edit->text().size()));
+ edit->clear();
+}
+
+void EncryptWalletAdvancedDialog::secureClearPassFields() {
+ SecureClearQLineAdvancedEdit(ui->keyEdit1);
+ SecureClearQLineAdvancedEdit(ui->keyEdit2);
+ SecureClearQLineAdvancedEdit(ui->passEdit1);
+ SecureClearQLineAdvancedEdit(ui->passEdit2);
+}
diff --git a/src/qt/forms/encryptwalletadvanceddialog.ui b/src/qt/forms/encryptwalletadvanceddialog.ui
new file mode 100644
--- /dev/null
+++ b/src/qt/forms/encryptwalletadvanceddialog.ui
@@ -0,0 +1,366 @@
+
+
+ AskEncryptAdvancedDialog
+
+
+
+ 0
+ 0
+ 615
+ 790
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 550
+ 0
+
+
+
+ Encrypt Wallet Advanced Dialog
+
+
+
+ QLayout::SetMinimumSize
+
+ -
+
+
+ This option is intended for Advanced users only. When wallets are encrypted a new Hierarchical Deterministic (HD) Master Seed is created for the wallet per BIP32 specifications. This dialog offers multiple options for how the new HD Master Seed is created, including: 1) Generating a new seed, 2) Manually providing a previously used seed to recover a wallet from backup, and 3) Manually creating a new seed using dice to ensure proper entropy.
+
+
+ Qt::RichText
+
+
+ true
+
+
+
+ -
+
+
+ QLayout::SetMaximumSize
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+
+ 75
+ true
+
+
+
+ HD Key Options
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 5
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+ Qt::RightToLeft
+
+
+ false
+
+
+ Generate New Master Seed
+
+
+
+ -
+
+
+ Generate new HD Master Seed and encrypt wallet. This is identical to the standard Encrypt Wallet option.
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 10
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+ Qt::RightToLeft
+
+
+ Recover Master Seed
+
+
+
+ -
+
+
+ Recover a previous wallet by restoring its Master Seed and encrypt wallet.
+
+
+ true
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 10
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 49
+
+
+
+ Qt::RightToLeft
+
+
+ Create New Master Seed
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Manually create new Master Seed and encrypt wallet. Performs the same steps as Generate New but uses the inputted seed instead generating a new seed randomly. <br><br><b>Input 128 six-sided dice rolls</b> with one digit entered for each roll. Discard rolls for sides '5' and '6' and only enter rolls for sides '1', '2','3' and '4'. Input each valid roll as a single character '1' to '4'. <b>Warning: You must make 128 successful dice rolls</b> to use this option, randomly typing digits provides very low entropy and will result in an insecure wallet.
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 80
+ 5
+
+
+
+
+ -
+
+
+ New passphrase
+
+
+
+ -
+
+
+ QLineEdit::Password
+
+
+
+ -
+
+
+ Repeat new passphrase
+
+
+
+ -
+
+
+ QLineEdit::Password
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 10
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 150
+ 10
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+ Enter 128 six sided Dice Rolls only entering rolls 1 to 4
+
+
+
+ -
+
+
+ Enter Master Seed from Backup
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Encryption Passphrase
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+ keyRadioButton1
+ keyRadioButton2
+ keyRadioButton3
+ keyEdit1
+ keyEdit2
+ passEdit1
+ passEdit2
+
+
+
+
+ buttonBox
+ accepted()
+ AskEncryptAdvancedDialog
+ accept()
+
+
+ 26
+ 437
+
+
+ 20
+ 20
+
+
+
+
+ buttonBox
+ rejected()
+ AskEncryptAdvancedDialog
+ reject()
+
+
+ 26
+ 437
+
+
+ 20
+ 20
+
+
+
+
+
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -81,6 +81,8 @@
/** Encrypt the wallet */
void encryptWallet(bool status);
+ /** Encrypt the wallet Advanced */
+ void encryptWalletAdvanced(bool status);
/** Backup the wallet */
void backupWallet();
/** Change encrypted wallet passphrase */
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -136,6 +136,11 @@
if (walletView) walletView->encryptWallet(status);
}
+void WalletFrame::encryptWalletAdvanced(bool status) {
+ WalletView *walletView = currentWalletView();
+ if (walletView) walletView->encryptWalletAdvanced(status);
+}
+
void WalletFrame::backupWallet() {
WalletView *walletView = currentWalletView();
if (walletView) walletView->backupWallet();
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -170,7 +170,8 @@
SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
// Wallet encryption
- bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);
+ bool setWalletEncrypted(bool encrypted, const SecureString &passphrase,
+ CKey *newSeed = nullptr);
// Passphrase only needed when unlocking
bool setWalletLocked(bool locked,
const SecureString &passPhrase = SecureString());
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -384,10 +384,11 @@
}
bool WalletModel::setWalletEncrypted(bool encrypted,
- const SecureString &passphrase) {
+ const SecureString &passphrase,
+ CKey *newSeed) {
if (encrypted) {
// Encrypt
- return wallet->EncryptWallet(passphrase);
+ return wallet->EncryptWallet(passphrase, newSeed);
} else {
// Decrypt -- TODO; not supported yet
return false;
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -99,6 +99,8 @@
int /*end*/);
/** Encrypt the wallet */
void encryptWallet(bool status);
+ /** Encrypt the wallet Advanced options */
+ void encryptWalletAdvanced(bool status);
/** Backup the wallet */
void backupWallet();
/** Change encrypted wallet passphrase */
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -8,6 +8,7 @@
#include "askpassphrasedialog.h"
#include "bitcoingui.h"
#include "clientmodel.h"
+#include "encryptwalletadvanceddialog.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "overviewpage.h"
@@ -109,10 +110,12 @@
SLOT(setEncryptionStatus(int)));
// Pass through transaction notifications
- connect(this, SIGNAL(incomingTransaction(QString, int, Amount, QString,
- QString, QString)),
- gui, SLOT(incomingTransaction(QString, int, Amount, QString,
- QString, QString)));
+ connect(this,
+ SIGNAL(incomingTransaction(QString, int, Amount, QString,
+ QString, QString)),
+ gui,
+ SLOT(incomingTransaction(QString, int, Amount, QString, QString,
+ QString)));
// Connect HD enabled state signal
connect(this, SIGNAL(hdEnabledStatusChanged(int)), gui,
@@ -258,6 +261,15 @@
updateEncryptionStatus();
}
+void WalletView::encryptWalletAdvanced(bool status) {
+ if (!walletModel) return;
+ EncryptWalletAdvancedDialog dlg(this);
+ dlg.setModel(walletModel);
+ dlg.exec();
+
+ updateEncryptionStatus();
+}
+
void WalletView::backupWallet() {
QString filename =
GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(),
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -660,9 +660,8 @@
}
CKey vchSecret;
if (!pwallet->GetKey(*keyID, vchSecret)) {
- throw JSONRPCError(RPC_WALLET_ERROR,
- "Private key for address " + strAddress +
- " is not known");
+ throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " +
+ strAddress + " is not known");
}
return CBitcoinSecret(vchSecret).ToString();
}
@@ -748,6 +747,11 @@
if (!masterKeyID.IsNull()) {
CKey key;
if (pwallet->GetKey(masterKeyID, key)) {
+
+ std::string masterseed = EncodeBase58(key.begin(), key.end());
+ file << "# private masterseed (save to recover HD Wallet): "
+ << masterseed << "\n\n";
+
CExtKey masterKey;
masterKey.SetMaster(key.begin(), key.size());
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -858,7 +858,8 @@
bool Unlock(const SecureString &strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString &strOldWalletPassphrase,
const SecureString &strNewWalletPassphrase);
- bool EncryptWallet(const SecureString &strWalletPassphrase);
+ bool EncryptWallet(const SecureString &strWalletPassphrase,
+ CKey *newSeed = nullptr);
void GetKeyBirthTimes(std::map &mapKeyBirth) const;
unsigned int ComputeTimeSmart(const CWalletTx &wtx) const;
@@ -888,7 +889,8 @@
const CBlockIndex *pIndex, int posInBlock,
bool fUpdate);
CBlockIndex *ScanForWalletTransactions(CBlockIndex *pindexStart,
- bool fUpdate = false);
+ bool fUpdate = false,
+ int64_t forceAll = 0);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime,
CConnman *connman) override;
@@ -1137,7 +1139,7 @@
bool IsHDEnabled();
/* Generates a new HD master key (will not be activated) */
- CPubKey GenerateNewHDMasterKey();
+ CPubKey GenerateNewHDMasterKey(CKey *newSeed = nullptr);
/**
* Set the current HD master key (will reset the chain child index counters)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -5,6 +5,8 @@
#include "wallet/wallet.h"
+#include "base58.h"
+#include "cashaddr.h"
#include "chain.h"
#include "checkpoints.h"
#include "config.h"
@@ -209,17 +211,15 @@
// child-index-range
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
if (internal) {
- chainChildKey.Derive(childKey,
- hdChain.nInternalChainCounter |
- BIP32_HARDENED_KEY_LIMIT);
+ chainChildKey.Derive(childKey, hdChain.nInternalChainCounter |
+ BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/1'/" +
std::to_string(hdChain.nInternalChainCounter) +
"'";
hdChain.nInternalChainCounter++;
} else {
- chainChildKey.Derive(childKey,
- hdChain.nExternalChainCounter |
- BIP32_HARDENED_KEY_LIMIT);
+ chainChildKey.Derive(childKey, hdChain.nExternalChainCounter |
+ BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/0'/" +
std::to_string(hdChain.nExternalChainCounter) +
"'";
@@ -731,7 +731,8 @@
}
}
-bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) {
+bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase,
+ CKey *newSeed) {
if (IsCrypted()) {
return false;
}
@@ -819,7 +820,7 @@
// If we are using HD, replace the HD master key (seed) with a new one.
if (IsHDEnabled()) {
CKey key;
- CPubKey masterPubKey = GenerateNewHDMasterKey();
+ CPubKey masterPubKey = GenerateNewHDMasterKey(newSeed);
// preserve the old chains version to not break backward
// compatibility
CHDChain oldChain = GetHDChain();
@@ -1532,9 +1533,25 @@
return nChange;
}
-CPubKey CWallet::GenerateNewHDMasterKey() {
+CPubKey CWallet::GenerateNewHDMasterKey(CKey *newSeed) {
CKey key;
- key.MakeNewKey(true);
+
+ if (newSeed == nullptr) {
+ // Standard path, no CKey was provided so generate a new one
+ key.MakeNewKey(true);
+ } else {
+ if (newSeed->IsValid()) {
+ // A CKey was provided and is valid, use it
+ LogPrintf("CWallet::GenerateNewHDMasterKey - Using given key\n");
+ key = *newSeed;
+ } else {
+ // A CKey was passed down but empty
+ // Generate a new key and send back to the dialog box for backup
+ LogPrintf("CWallet::GenerateNewHDMasterKey - Generating new key\n");
+ key.MakeNewKey(true);
+ *newSeed = key;
+ }
+ }
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
@@ -1707,7 +1724,8 @@
* the range doesn't include chainActive.Tip().
*/
CBlockIndex *CWallet::ScanForWalletTransactions(CBlockIndex *pindexStart,
- bool fUpdate) {
+ bool fUpdate,
+ int64_t forceAll) {
LOCK2(cs_main, cs_wallet);
int64_t nNow = GetTime();
@@ -1717,9 +1735,12 @@
// No need to read and scan block, if block was created before our wallet
// birthday (as adjusted for block time variability)
- while (pindex && nTimeFirstKey &&
- (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) {
- pindex = chainActive.Next(pindex);
+ // Skip this adjustment if user requested a complete scan with -rescan=1
+ if (forceAll == 0) {
+ while (pindex && nTimeFirstKey &&
+ (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) {
+ pindex = chainActive.Next(pindex);
+ }
}
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan on
@@ -4036,8 +4057,9 @@
_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));
strUsage += HelpMessageOpt(
- "-rescan",
- _("Rescan the block chain for missing wallet transactions on startup"));
+ "-rescan", _("Rescan the block chain for missing wallet transactions "
+ "on startup, use "
+ "-rescan=1 to force rescanning from the genesis block"));
strUsage += HelpMessageOpt(
"-salvagewallet",
_("Attempt to recover private keys from a corrupt wallet on startup"));
@@ -4082,8 +4104,9 @@
"-zapwallettxes=",
_("Delete all wallet transactions and only recover those parts of the "
"blockchain through -rescan on startup") +
- " " + _("(1 = keep tx meta data e.g. account owner and payment "
- "request information, 2 = drop tx meta data)"));
+ " " +
+ _("(1 = keep tx meta data e.g. account owner and payment "
+ "request information, 2 = drop tx meta data)"));
if (showDebug) {
strUsage += HelpMessageGroup(_("Wallet debugging/testing options:"));
@@ -4273,7 +4296,8 @@
chainActive.Height() - pindexRescan->nHeight,
pindexRescan->nHeight);
nStart = GetTimeMillis();
- walletInstance->ScanForWalletTransactions(pindexRescan, true);
+ walletInstance->ScanForWalletTransactions(pindexRescan, true,
+ gArgs.GetArg("-rescan", 0));
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
walletInstance->SetBestChain(chainActive.GetLocator());
walletInstance->dbw->IncrementUpdateCounter();