diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 24f2133b9..86a8e4023 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -101,6 +101,7 @@ QT_FORMS_UI = \ qt/forms/editaddressdialog.ui \ qt/forms/enterunlockcode.ui \ qt/forms/exportwalletdialog.ui \ + qt/forms/importwalletdialog.ui \ qt/forms/helpmessagedialog.ui \ qt/forms/intro.ui \ qt/forms/modaloverlay.ui \ @@ -133,6 +134,7 @@ QT_MOC_CPP = \ qt/moc_csvmodelwriter.cpp \ qt/moc_editaddressdialog.cpp \ qt/moc_exportwalletdialog.cpp \ + qt/moc_importwalletdialog.cpp \ qt/moc_enterunlockcode.cpp \ qt/moc_guiutil.cpp \ qt/moc_intro.cpp \ @@ -207,6 +209,7 @@ MERIT_QT_H = \ qt/csvmodelwriter.h \ qt/editaddressdialog.h \ qt/exportwalletdialog.h \ + qt/importwalletdialog.h \ qt/enterunlockcode.h \ qt/guiconstants.h \ qt/guiutil.h \ @@ -327,6 +330,7 @@ MERIT_QT_BASE_CPP = \ qt/intro.cpp \ qt/enterunlockcode.cpp \ qt/exportwalletdialog.cpp \ + qt/importwalletdialog.cpp \ qt/modaloverlay.cpp \ qt/networkstyle.cpp \ qt/notificator.cpp \ diff --git a/src/crypto/mnemonic/mnemonic.cpp b/src/crypto/mnemonic/mnemonic.cpp index 723b273cb..124abfcce 100644 --- a/src/crypto/mnemonic/mnemonic.cpp +++ b/src/crypto/mnemonic/mnemonic.cpp @@ -21,6 +21,27 @@ namespace mnemonic } } + WordList MnemonicStringToWords(const std::string& mnemonic) + { + std::stringstream s; + s << mnemonic; + + WordList words; + while(s.good()) { + std::string word; + s >> word; + + if(!word.empty()) { + words.push_back(word); + } + } + return words; + } + + bool IsAValidMnemonic(const WordList& words) { + return words.size() == MNEMONIC_WORD_COUNT; + } + std::array MnemonicToSeed(const WordList& mnemonic, const std::string& passphrase) { return MnemonicToSeed(Unwords(mnemonic), passphrase); @@ -69,6 +90,8 @@ namespace mnemonic WordList mnemonic(MNEMONIC_WORD_COUNT); std::transform(inds.begin(), inds.end(), mnemonic.begin(), [&dict](const int& i) { return dict[i]; }); + + assert(IsAValidMnemonic(mnemonic)); return mnemonic; } } diff --git a/src/crypto/mnemonic/mnemonic.h b/src/crypto/mnemonic/mnemonic.h index 76a3f1987..9e1ad0342 100644 --- a/src/crypto/mnemonic/mnemonic.h +++ b/src/crypto/mnemonic/mnemonic.h @@ -17,6 +17,8 @@ namespace mnemonic static constexpr size_t ENTROPY_BYTES = 16; std::array MnemonicToSeed(const WordList& mnemonic, const std::string& passphrase = ""); std::array MnemonicToSeed(const std::string& mnemonic, const std::string& passphrase = ""); + bool IsAValidMnemonic(const WordList& words); + WordList MnemonicStringToWords(const std::string& mnemonic); std::string Unwords(const WordList& phrase); WordList Entropy2Mnemonic(const std::vector& entropy, const language::Dictionary& dict); } diff --git a/src/qt/enterunlockcode.cpp b/src/qt/enterunlockcode.cpp index fcd84c226..61fe4be95 100644 --- a/src/qt/enterunlockcode.cpp +++ b/src/qt/enterunlockcode.cpp @@ -4,6 +4,7 @@ #include "enterunlockcode.h" #include "ui_enterunlockcode.h" +#include "importwalletdialog.h" #include "guiutil.h" @@ -25,11 +26,13 @@ userClosed(false) connect(ui->aliasTextInput, SIGNAL(textChanged(QString)), this, SLOT(aliasChanged(QString))); connect(this, SIGNAL(CanSubmitChanged(bool)), ui->submitButton, SLOT(setEnabled(bool))); connect(ui->submitButton, SIGNAL(clicked()), this, SLOT(submit())); + connect(ui->importButton, SIGNAL(clicked()), this, SLOT(importWallet())); if (parent) { parent->installEventFilter(this); raise(); } + ui->importButton->setEnabled(false); setVisible(false); } @@ -90,6 +93,7 @@ void EnterUnlockCode::showHide(bool hide, bool userRequested) void EnterUnlockCode::setModel(WalletModel *model) { this->walletModel = model; + ui->importButton->setEnabled(true); } extern CTxDestination LookupDestination(const std::string& address); @@ -164,3 +168,17 @@ void EnterUnlockCode::submit() } } } + +void EnterUnlockCode::importWallet() +{ + if(!walletModel) { + return; + } + + ImportWalletDialog importWalletDialog{this, walletModel}; + importWalletDialog.exec(); + + if(importWalletDialog.result() == QDialog::Accepted) { + Q_EMIT WalletReferred(); + } +} diff --git a/src/qt/enterunlockcode.h b/src/qt/enterunlockcode.h index b1629ce28..28f1ac6a2 100644 --- a/src/qt/enterunlockcode.h +++ b/src/qt/enterunlockcode.h @@ -10,6 +10,8 @@ namespace Ui { class EnterUnlockCode; } +class ImportWalletDialog; + /** Modal overlay to display information about the chain-sync state */ class EnterUnlockCode : public QWidget { @@ -31,6 +33,7 @@ public Q_SLOTS: // will show or hide the modal layer void showHide(bool hide = false, bool userRequested = false); bool isLayerVisible() const { return layerIsVisible; } + void importWallet(); protected: bool eventFilter(QObject * obj, QEvent * ev); diff --git a/src/qt/exportwalletdialog.cpp b/src/qt/exportwalletdialog.cpp index 163050223..57509de46 100644 --- a/src/qt/exportwalletdialog.cpp +++ b/src/qt/exportwalletdialog.cpp @@ -13,6 +13,7 @@ #include "walletmodel.h" #include "exportwalletdialog.h" #include "ui_exportwalletdialog.h" +#include "util.h" ExportWalletDialog::ExportWalletDialog(QWidget *parent, WalletModel *model) : QDialog(parent), @@ -34,11 +35,22 @@ ExportWalletDialog::~ExportWalletDialog() delete ui; } +void ExportWalletDialog::closeEvent(QCloseEvent *event) { + hideCode(); + QDialog::closeEvent(event); +} + void ExportWalletDialog::onCancelClicked() { + hideCode(); reject(); } +void ExportWalletDialog::hideCode() { + qrCodeIsVisible = false; + setQRCodeVisibility(); +} + void ExportWalletDialog::onShowClicked() { qrCodeIsVisible = !qrCodeIsVisible; @@ -49,6 +61,12 @@ void ExportWalletDialog::setQRCodeVisibility() { if(qrCodeIsVisible) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) { + // Unlock wallet was cancelled + return; + } + ui->lblQRCode->setText(""); #ifdef USE_QRCODE bool livenet = Params().NetworkIDString() == CBaseChainParams::MAIN; diff --git a/src/qt/exportwalletdialog.h b/src/qt/exportwalletdialog.h index aa5100b76..114744442 100644 --- a/src/qt/exportwalletdialog.h +++ b/src/qt/exportwalletdialog.h @@ -17,15 +17,18 @@ class ExportWalletDialog : public QDialog explicit ExportWalletDialog(QWidget *parent, WalletModel *model); ~ExportWalletDialog(); +protected: + void closeEvent(QCloseEvent *event); + private Q_SLOTS: void onCancelClicked(); void onShowClicked(); - private: WalletModel *walletModel; Ui::ExportWalletDialog *ui; bool qrCodeIsVisible = false; + void hideCode(); void setQRCodeVisibility(); }; diff --git a/src/qt/forms/enterunlockcode.ui b/src/qt/forms/enterunlockcode.ui index df61bf9cd..197b7c91b 100644 --- a/src/qt/forms/enterunlockcode.ui +++ b/src/qt/forms/enterunlockcode.ui @@ -66,7 +66,7 @@ QLabel { color: rgb(40,40,40); } - + 0 @@ -260,7 +260,7 @@ QLabel { color: rgb(40,40,40); } <html><head/><body><p>Other users of <span style=" font-weight:600;">Merit</span> can use your <span style=" font-weight:600;">Public Alias</span> instead of an address to send you funds. This alias is <span style=" font-style:italic;">optional</span>. An alias cannot be changed for an address once it is chosen.</p><p><br/></p><p>The alias must be <span style=" font-weight:600;">globally unique</span>, nobody else can use your alias once it is commited to the Merit Blockchain.</p><p><br/></p><p>For more information, visit: <a href="http://merit.me/aliases.html"><span style=" text-decoration: underline; color:#0000ff;">merit.me/aliases.html</span></a></p></body></html> - Choose an optional Public Alias. + Choose a Public Alias. true @@ -307,11 +307,33 @@ QLabel { color: rgb(40,40,40); } + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 10 + + + + false + + + 0 + 40 + + 12 @@ -322,6 +344,80 @@ QLabel { color: rgb(40,40,40); } + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 15 + + + + + + + + 10 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 9 + + + + Import Existing Wallet + + + + + + + + + Qt::Vertical + + + + 20 + 80 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/qt/forms/importwalletdialog.ui b/src/qt/forms/importwalletdialog.ui new file mode 100644 index 000000000..56e7df446 --- /dev/null +++ b/src/qt/forms/importwalletdialog.ui @@ -0,0 +1,134 @@ + + + ImportWalletDialog + + + + 0 + 0 + 339 + 262 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + 16 + 75 + true + + + + <html><head/><body><p><span style=" color:#384c62;">Import Wallet</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p align="justify"><span style=" font-weight:600; color:#aa0000;">Warning: </span>Make sure you are in a secure location where people can not see your screen!</p></body></html> + + + Qt::AlignJustify|Qt::AlignVCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p><span style=" font-size:12pt; font-weight:600; color:#384c62;">Type your Mnemonic Phrase Here</span></p></body></html> + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + false + + + + + + + Import + + + + + + + 0 + + + + + Cancel + + + + + + + + + <html><head/><body><p><span style=" font-size:10pt;">Importing... This may take a moment.</span></p></body></html> + + + + + + + + diff --git a/src/qt/importwalletdialog.cpp b/src/qt/importwalletdialog.cpp new file mode 100644 index 000000000..26a2cffa3 --- /dev/null +++ b/src/qt/importwalletdialog.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "config/merit-config.h" /* for USE_QRCODE */ +#ifdef USE_QRCODE + #include +#endif + +#include "base58.h" +#include "guiconstants.h" +#include "qrutil.h" +#include "walletmodel.h" +#include "importwalletdialog.h" +#include "ui_interface.h" +#include "ui_importwalletdialog.h" +#include "crypto/mnemonic/mnemonic.h" + +ImportWalletDialog::ImportWalletDialog(QWidget *parent, WalletModel *wmodel) : + QDialog(parent), + model(wmodel), + ui(new Ui::ImportWalletDialog) +{ + ui->setupUi(this); + + connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(OnCancelClicked())); + connect(ui->importButton, SIGNAL(clicked()), this, SLOT(ImportWallet())); + connect(ui->mnemonic, SIGNAL(textChanged()), this, SLOT(UpdateImportButton())); + + ui->importButton->setEnabled(false); + ui->progressTitle->setVisible(false); +} + +ImportWalletDialog::~ImportWalletDialog() +{ + delete ui; +} + +void ImportWalletDialog::OnCancelClicked() +{ + reject(); +} + +void ImportWalletDialog::UpdateImportButton() +{ + auto mnemonic = ui->mnemonic->toPlainText().toStdString(); + boost::trim(mnemonic); + const bool enabled = model->IsAValidMnemonic(mnemonic); + + if(enabled) { + ui->mnemonic->setStyleSheet("QPlainTextEdit { background-color: rgb(128, 255, 128) }"); + } else { + ui->mnemonic->setStyleSheet(""); + } + + ui->importButton->setEnabled(enabled); +} + +void ImportWalletDialog::ImportWallet() +{ + assert(model); + ui->cancelButton->setVisible(false); + ui->importButton->setVisible(false); + ui->progressTitle->setVisible(true); + + QTimer::singleShot(500, this, SLOT(DoImport())); +} + +void ImportWalletDialog::DoImport() +{ + auto mnemonic = ui->mnemonic->toPlainText().toStdString(); + boost::trim(mnemonic); + + import_result = std::async(std::launch::async, [this,mnemonic]() { + return model->ImportMnemonicAsMaster(mnemonic); + }); + + QTimer::singleShot(500, this, SLOT(CheckImport())); +} + +void ImportWalletDialog::CheckImport() +{ + if(import_result.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { + QTimer::singleShot(500, this, SLOT(CheckImport())); + return; + } + + bool success = import_result.get(); + if(!success) { + QMessageBox::critical( + this, + tr("Error importing wallet"), + tr("Unable to import the wallet with the mnemonic given")); + reject(); + return; + } + + accept(); + return; +} diff --git a/src/qt/importwalletdialog.h b/src/qt/importwalletdialog.h new file mode 100644 index 000000000..d384ac0dc --- /dev/null +++ b/src/qt/importwalletdialog.h @@ -0,0 +1,34 @@ +#ifndef MERIT_QT_IMPORTWALLETDIALOG_H +#define MERIT_QT_IMPORTWALLETDIALOG_H + +#include +#include + +class WalletModel; + +namespace Ui { + class ImportWalletDialog; +} + +class ImportWalletDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ImportWalletDialog(QWidget *parent, WalletModel *model); + ~ImportWalletDialog(); + +private Q_SLOTS: + void OnCancelClicked(); + void ImportWallet(); + void UpdateImportButton(); + void DoImport(); + void CheckImport(); + +private: + WalletModel *model; + Ui::ImportWalletDialog *ui; + std::future import_result; +}; + +#endif diff --git a/src/qt/meritgui.cpp b/src/qt/meritgui.cpp index c34e46d26..3a36e0fb7 100644 --- a/src/qt/meritgui.cpp +++ b/src/qt/meritgui.cpp @@ -15,6 +15,7 @@ #include "guiutil.h" #include "enterunlockcode.h" #include "exportwalletdialog.h" +#include "importwalletdialog.h" #include "miner.h" #include "modaloverlay.h" #include "networkstyle.h" @@ -117,6 +118,7 @@ MeritGUI::MeritGUI(const PlatformStyle *_platformStyle, const NetworkStyle *netw encryptWalletAction(nullptr), backupWalletAction(nullptr), exportWalletQRAction(nullptr), + importWalletAction(nullptr), changePassphraseAction(nullptr), aboutQtAction(nullptr), openRPCConsoleAction(nullptr), @@ -379,13 +381,21 @@ void MeritGUI::createActions() #ifdef USE_QRCODE exportWalletQRAction = new QAction(platformStyle->TextColorIcon(":/icons/filesave"), tr("&Export Wallet..."), this); exportWalletQRAction->setStatusTip(tr("Export wallet to another device")); + exportWalletQRAction->setVisible(false); #endif + importWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Import Wallet..."), this); + importWalletAction->setStatusTip(tr("Import wallet using Mnemonic")); + changePassphraseAction = new QAction(platformStyle->TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this); changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); + signMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/edit"), tr("Sign &message..."), this); signMessageAction->setStatusTip(tr("Sign messages with your Merit addresses to prove you own them")); + signMessageAction->setVisible(false); + verifyMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Verify message..."), this); verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Merit addresses")); + verifyMessageAction->setVisible(false); openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); @@ -433,6 +443,7 @@ void MeritGUI::createActions() connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked())); connect(startMiningAction, SIGNAL(triggered()), this, SLOT(startMiningClicked())); connect(stopMiningAction, SIGNAL(triggered()), this, SLOT(stopMiningClicked())); + connect(importWalletAction, SIGNAL(triggered()), this, SLOT(showImportWallet())); #ifdef USE_QRCODE connect(exportWalletQRAction, SIGNAL(triggered()), this, SLOT(showExportWallet())); #endif @@ -458,6 +469,7 @@ void MeritGUI::createMenuBar() if(walletFrame) { file->addAction(openAction); + file->addAction(importWalletAction); file->addAction(backupWalletAction); #ifdef USE_QRCODE file->addAction(exportWalletQRAction); @@ -588,13 +600,6 @@ bool MeritGUI::addWallet(const QString& name, WalletModel *_walletModel) walletModel = _walletModel; assert(walletModel); - hasMnemonic = walletModel->hasMnemonic(); - #ifdef USE_QRCODE - if(hasMnemonic) - exportWalletDialog = new ExportWalletDialog(this, walletModel); - exportWalletQRAction->setVisible(hasMnemonic); - #endif - isReferred = walletModel->IsReferred(); setWalletActionsEnabled(true, isReferred); connect(walletModel, SIGNAL(isConfirmedChanged(bool)), this, SLOT(setMiningEnabled(bool))); @@ -634,6 +639,9 @@ void MeritGUI::walletReferred() { setWalletActionsEnabled(true); enterUnlockCode->showHide(true, true); + if(walletFrame) { + walletFrame->UpdateOverviewPage(); + } } #endif // ENABLE_WALLET @@ -644,6 +652,16 @@ void MeritGUI::setWalletActionsEnabled(bool enabled) void MeritGUI::setWalletActionsEnabled(bool enabled, bool isReferred) { + if(walletModel) { + hasMnemonic = walletModel->hasMnemonic(); +#ifdef USE_QRCODE + if(hasMnemonic) + exportWalletDialog = new ExportWalletDialog(this, walletModel); + exportWalletQRAction->setVisible(hasMnemonic); +#endif + } + + importWalletAction->setVisible(!isReferred); overviewAction->setEnabled(enabled); sendCoinsAction->setEnabled(enabled && isReferred); sendCoinsMenuAction->setEnabled(enabled && isReferred); @@ -769,6 +787,19 @@ void MeritGUI::showExportWallet() #endif } +void MeritGUI::showImportWallet() +{ + if(!walletModel) { + return; + } + + ImportWalletDialog importWalletDialog{this, walletModel}; + importWalletDialog.exec(); + if(importWalletDialog.result() == QDialog::Accepted) { + walletReferred(); + } +} + #ifdef ENABLE_WALLET void MeritGUI::openClicked() { diff --git a/src/qt/meritgui.h b/src/qt/meritgui.h index a594cf2e5..6c65fec9d 100644 --- a/src/qt/meritgui.h +++ b/src/qt/meritgui.h @@ -33,6 +33,7 @@ class HelpMessageDialog; class ModalOverlay; class EnterUnlockCode; class ExportWalletDialog; +class ImportWalletDialog; QT_BEGIN_NAMESPACE class QAction; @@ -113,6 +114,7 @@ class MeritGUI : public QMainWindow QAction *encryptWalletAction; QAction *backupWalletAction; QAction *exportWalletQRAction; + QAction *importWalletAction; QAction *changePassphraseAction; QAction *aboutQtAction; QAction *openRPCConsoleAction; @@ -181,6 +183,7 @@ public Q_SLOTS: void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers); void setMiningEnabled(bool enabled); void showExportWallet(); + void showImportWallet(); /** Notify the user of an event from the core network or transaction handling code. @param[in] title the message box / notification title diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 5803a8008..a6191db3c 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -554,10 +554,24 @@ void OverviewPage::setBalance( UpdateInvitationStatus(); } -void OverviewPage::setYourCommunity( - const QString &alias, - const QString &address) +void OverviewPage::Update() { + assert(walletModel); + + UpdateInvitationStatus(); + UpdateNetworkView(); + UpdateInviteRequestView(); +} + +void OverviewPage::UpdateCommunityView() +{ + if(!walletModel) { + return; + } + + auto alias = walletModel->GetAlias(); + auto address = walletModel->GetUnlockCode(); + if(alias.length() > 0) { ui->aliasTitleLabel->setHidden(false); @@ -649,9 +663,7 @@ void OverviewPage::setWalletModel(WalletModel *model) model->getWatchImmatureBalance(), model->getBalance(nullptr, true)); - setYourCommunity( - model->GetAlias(), - model->GetUnlockCode()); + UpdateCommunityView(); connect( model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), @@ -746,6 +758,9 @@ void OverviewPage::UpdateInviteRequestView() assert(pendingRequestsFilter); assert(approvedRequestsFilter); + + pendingRequestsFilter->invalidate(); + approvedRequestsFilter->invalidate(); const bool has_requests = pendingRequestsFilter->rowCount() > 0; const bool has_approved = approvedRequestsFilter->rowCount() > 0; @@ -813,9 +828,7 @@ void OverviewPage::UpdateNetworkView() return; } - setYourCommunity( - walletModel->GetAlias(), - walletModel->GetUnlockCode()); + UpdateCommunityView(); auto ref_model = walletModel->getReferralListModel(); if(ref_model) { diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index ddd1c0a63..966ee31c8 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -49,9 +49,9 @@ public Q_SLOTS: CAmount watchUnconfBalance, CAmount watchImmatureBalance, CAmount inviteBalance); - void setYourCommunity( - const QString &alias, - const QString &address); + + void Update(); + void UpdateCommunityView(); void UpdateInvitationStatus(); void UpdateInviteRequestView(); void UpdateNetworkView(); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index a9cb17cc9..efd45d1ab 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -202,3 +202,10 @@ void WalletFrame::outOfSyncWarningClicked() { Q_EMIT requestedSyncWarningInfo(); } + +void WalletFrame::UpdateOverviewPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->UpdateOverviewPage(); +} diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 1eb912d39..9aebb90a1 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -92,6 +92,7 @@ public Q_SLOTS: void usedReceivingAddresses(); /** Pass on signal over requested out-of-sync-warning information */ void outOfSyncWarningClicked(); + void UpdateOverviewPage(); }; #endif // MERIT_QT_WALLETFRAME_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f5119fe0c..58efd07a3 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -900,3 +900,21 @@ bool WalletModel::getDefaultWalletRbf() const { return fWalletRbf; } + +bool WalletModel::IsAValidMnemonic(const std::string& mnemonic) +{ + assert(wallet); + return wallet->IsAValidMnemonic(mnemonic); +} + +bool WalletModel::ImportMnemonicAsMaster(const std::string& mnemonic) +{ + assert(wallet); + return wallet->ImportMnemonicAsMaster(mnemonic); +} + +bool WalletModel::CryptedWalletNeedsNewPassphrase() const +{ + assert(wallet); + return wallet->CryptedWalletNeedsNewPassphrase(); +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 8c59e5b4f..343306b79 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -238,6 +238,10 @@ class WalletModel : public QObject bool getDefaultWalletRbf() const; + bool IsAValidMnemonic(const std::string& mnemonic); + bool ImportMnemonicAsMaster(const std::string& mnemonic); + bool CryptedWalletNeedsNewPassphrase() const; + private: CWallet *wallet; bool fHaveWatchOnly; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 2ac71b505..23f7506ec 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -65,6 +65,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent): // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo())); + connect(this, SIGNAL(DoUpdateOverviewPage()), overviewPage, SLOT(Update())); // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); @@ -150,6 +151,8 @@ void WalletView::setWalletModel(WalletModel *_walletModel) // Show progress dialog connect(_walletModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int))); + + QTimer::singleShot(3000, this, SLOT(CheckChangePassphrase())); } } @@ -286,6 +289,31 @@ void WalletView::changePassphrase() dlg.exec(); } +void WalletView::CheckChangePassphrase() +{ + assert(walletModel); + if(!walletModel->IsReferred()) { + return; + } + + if(walletModel->CryptedWalletNeedsNewPassphrase()) { + QMessageBox msgBox{QMessageBox::Question, + "Insecure Mnemonic", + "Versions prior to 0.5.2 did not properly encrypt the mnemonic. " + "It looks like your wallet needs to be upgraded. " + "We recommend you change your passphrase to secure your wallet. " + "Do you want to change your passphrase now?", + QMessageBox::Yes | QMessageBox::No, + this}; + msgBox.setStyleSheet(QString("QMessageBox { background-color: white; }")); + auto ret = msgBox.exec(); + + if(ret == QMessageBox::Yes) { + changePassphrase(); + } + } +} + void WalletView::unlockWallet() { if(!walletModel) @@ -346,3 +374,8 @@ void WalletView::requestedSyncWarningInfo() { Q_EMIT outOfSyncWarningClicked(); } + +void WalletView::UpdateOverviewPage() +{ + Q_EMIT DoUpdateOverviewPage(); +} diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 09328171f..6465fdea5 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -97,6 +97,8 @@ public Q_SLOTS: void backupWallet(); /** Change encrypted wallet passphrase */ void changePassphrase(); + /** Check to see if passphrase change is nessasary*/ + void CheckChangePassphrase(); /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); @@ -113,6 +115,7 @@ public Q_SLOTS: /** User has requested more information about the out of sync state */ void requestedSyncWarningInfo(); + void UpdateOverviewPage(); Q_SIGNALS: /** Signal that we want to show the main window */ @@ -135,6 +138,7 @@ public Q_SLOTS: /** Notify that the out of sync warning icon has been pressed */ void outOfSyncWarningClicked(); + void DoUpdateOverviewPage(); }; #endif // MERIT_QT_WALLETVIEW_H diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 47d665bcb..d32893183 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -110,7 +110,11 @@ bool CCrypter::Decrypt(const std::vector& vchCiphertext, CKeyingM } -static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext) +bool EncryptSecret( + const CKeyingMaterial& vMasterKey, + const CKeyingMaterial &vchPlaintext, + const uint256& nIV, + std::vector &vchCiphertext) { CCrypter cKeyCrypter; std::vector chIV(WALLET_CRYPTO_IV_SIZE); @@ -120,7 +124,11 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); } -static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) +bool DecryptSecret( + const CKeyingMaterial& vMasterKey, + const std::vector& vchCiphertext, + const uint256& nIV, + CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; std::vector chIV(WALLET_CRYPTO_IV_SIZE); @@ -206,6 +214,18 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) return true; } +bool CCryptoKeyStore::DecryptSecret( + const std::vector& ciphertext, + const uint256& IV, + CKeyingMaterial& plaintext) const +{ + if (!IsCrypted()) { + return false; + } + + return ::DecryptSecret(vMasterKey, ciphertext, IV, plaintext); +} + bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 622df245e..39c72fc23 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -133,6 +133,11 @@ class CCryptoKeyStore : public CBasicKeyStore bool Unlock(const CKeyingMaterial& vMasterKeyIn); CryptedKeyMap mapCryptedKeys; + bool DecryptSecret( + const std::vector& vchCiphertext, + const uint256& nIV, + CKeyingMaterial& vchPlaintext) const; + public: CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false) { @@ -194,4 +199,17 @@ class CCryptoKeyStore : public CBasicKeyStore boost::signals2::signal NotifyStatusChanged; }; +bool EncryptSecret( + const CKeyingMaterial& vMasterKey, + const CKeyingMaterial &vchPlaintext, + const uint256& nIV, + std::vector &vchCiphertext); + +bool DecryptSecret( + const CKeyingMaterial& vMasterKey, + const std::vector& vchCiphertext, + const uint256& nIV, + CKeyingMaterial& vchPlaintext); + + #endif // MERIT_WALLET_CRYPTER_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 197ec37ff..2adc5414c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4151,6 +4151,7 @@ UniValue getmnemonic(const JSONRPCRequest& request) throw std::runtime_error( "getmnemonic\n" "Returns an object containing a mnemonic phrase if one exists used to recover your wallet.\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nResult:\n" "{\n" " \"mnemonic\": xxxxx, (string) mnemonic phrase used to recover your wallet\n" @@ -4162,12 +4163,19 @@ UniValue getmnemonic(const JSONRPCRequest& request) ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); + + if(pwallet->CryptedWalletNeedsNewPassphrase()) { + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, + "Error: You have an encrypted wallet but your mnemonic is not" + " encrypted. Please change the passphrase using walletpassphrasechange to secure it."); + } UniValue obj(UniValue::VOBJ); CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID; if (!masterKeyID.IsNull()) { - std::string mnemonic = pwallet->mapKeyMetadata[masterKeyID].mnemonic; + auto mnemonic = pwallet->GetMnemonic(); if(mnemonic.length() == 0) throw JSONRPCError(RPC_WALLET_NO_MNEMONIC, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1318d0c3f..44b602233 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -187,6 +187,56 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } +bool CWallet::ImportMnemonicAsMaster( + const std::string& mnemonic, + const std::string& passphrase) +{ + if(!IsAValidMnemonic(mnemonic)) { + return false; + } + + CWalletDB walletdb(*dbw); + LOCK(cs_wallet); + assert(IsHDEnabled()); + + auto words = mnemonic::MnemonicStringToWords(mnemonic); + auto master_pubkey = GenerateMasterKeyFromMnemonic(words, passphrase); + + if (!SetHDMasterKey(master_pubkey)) { + LogPrintf("Failed to set master hd key\n"); + return false; + } + + auto derived_pubkey = GenerateNewKey(walletdb); + CKeyPool keypool(derived_pubkey, true); + + auto referral = prefviewcache->GetReferral(derived_pubkey.GetID()); + if(!referral) { + LogPrintf("Failed to find the referral on the blockchain\n"); + return false; + } + + AddReferralAddressPubKey(referral->GetAddress(), referral->pubkey.GetID()); + + referral::ReferralTx rtx{MakeReferralRef(*referral), true}; + if (!AddToWallet(rtx)) { + LogPrintf("Failed to add the referral to the wallet.\n"); + return false; + } + + int64_t index = ++m_max_keypool_index; + + if (!walletdb.WritePool(index, keypool)) { + LogPrintf("Failed write the keypool.\n"); + return false; + } + + LoadKeyPool(index, keypool); + RescanFromTime(TIMESTAMP_MIN, true /* update */); + + return true; +} + referral::ReferralRef CWallet::Unlock(const referral::Address& parentAddress, std::string alias) { // check wallet is not unlocked yet @@ -277,7 +327,15 @@ CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb) CKeyMetadata metadata(nCreationTime); if(mapKeyMetadata[hdChain.masterKeyID].nVersion >= CKeyMetadata::VERSION_WITH_MNEMONIC) { - DeriveNewBIP44ChildKey(walletdb, metadata, secret); + DeriveNewBIP44ChildKey( + mapKeyMetadata[hdChain.masterKeyID].mnemonic, + metadata.hdKeypath, + secret, + hdChain.nInternalChainCounter); + metadata.hdMasterKeyID = hdChain.masterKeyID; + // update the chain model in the database + if (!walletdb.WriteHDChain(hdChain)) + throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } else { DeriveNewChildKey(walletdb, metadata, secret); } @@ -333,11 +391,15 @@ void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKe } // TODO: make a more generic method to derive based on a given keypath -void CWallet::DeriveNewBIP44ChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret) +void CWallet::DeriveNewBIP44ChildKey( + const std::string& mnemonic, + std::string& hdKeypath, + CKey& secret, + uint32_t& hdchain_counter) { // this method uses a fixed keypath scheme of m/44'/0'/0'/0/k // m/44'/1'/0'/0/k for testnet - const auto seed = mnemonic::MnemonicToSeed(mapKeyMetadata[hdChain.masterKeyID].mnemonic); + const auto seed = mnemonic::MnemonicToSeed(mnemonic); CExtKey masterKey; //hd master key m CExtKey changeKey; //key at m/44'/0'/0'/0' @@ -360,15 +422,11 @@ void CWallet::DeriveNewBIP44ChildKey(CWalletDB &walletdb, CKeyMetadata& metadata // derive child key at next index, skip keys already known to the wallet do { - changeKey.Derive(childKey, hdChain.nInternalChainCounter); - metadata.hdKeypath = (livenet ? "m/44'/0'/0'/0" : "m/44'/1'/0'/0/") + std::to_string(hdChain.nInternalChainCounter); - hdChain.nInternalChainCounter++; + changeKey.Derive(childKey, hdchain_counter); + hdKeypath = (livenet ? "m/44'/0'/0'/0" : "m/44'/1'/0'/0/") + std::to_string(hdchain_counter); + hdchain_counter++; } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; - metadata.hdMasterKeyID = hdChain.masterKeyID; - // update the chain model in the database - if (!walletdb.WriteHDChain(hdChain)) - throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const CPubKey &pubkey) @@ -418,16 +476,18 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; + { LOCK(cs_wallet); - if (pwalletdbEncryption) + if (pwalletdbEncryption) { return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); - else + } else { return CWalletDB(*dbw).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); + } } } @@ -518,6 +578,13 @@ bool CWallet::LoadWatchOnly(const CScript &dest) return CCryptoKeyStore::AddWatchOnly(dest); } +bool CWallet::CryptedWalletNeedsNewPassphrase() const +{ + const auto p = mapKeyMetadata.find(hdChain.masterKeyID); + return IsCrypted() && p != mapKeyMetadata.end() && + p->second.nVersion < CKeyMetadata::VERSION_WITH_SECURE_MNEMONIC; +} + bool CWallet::Unlock(const SecureString& strWalletPassphrase) { CCrypter crypter; @@ -554,8 +621,16 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) return false; - if (CCryptoKeyStore::Unlock(_vMasterKey)) - { + + if(CryptedWalletNeedsNewPassphrase()) { + CWalletDB walletdb{*dbw}; + if(!EncryptMnemonic(walletdb, _vMasterKey)) { + return false; + } + } + + if (CCryptoKeyStore::Unlock(_vMasterKey)) { + int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); @@ -745,6 +820,48 @@ void CWallet::AddToSpends(const uint256& wtxid) AddToSpends(txin.prevout, wtxid); } +bool CWallet::EncryptMnemonic(CWalletDB& walletdb, CKeyingMaterial& master_key) +{ + auto& meta = mapKeyMetadata[hdChain.masterKeyID]; + auto& mnemonic = meta.mnemonic; + + CKeyingMaterial secret{mnemonic.begin(), mnemonic.end()}; + + auto master_key_id_string = hdChain.masterKeyID.ToString(); + auto master_key_id_hash = Hash(master_key_id_string.begin(), master_key_id_string.end()); + + std::vector encrypted_mnemonic; + if (!EncryptSecret(master_key, secret, master_key_id_hash, encrypted_mnemonic)) + return false; + + mnemonic.resize(encrypted_mnemonic.size()); + std::copy(encrypted_mnemonic.begin(), encrypted_mnemonic.end(), mnemonic.begin()); + + CPubKey hdkey; + if(IsCrypted()) { + + auto p = mapCryptedKeys.find(hdChain.masterKeyID); + if (p == mapCryptedKeys.end()) { + return false; + } + hdkey = p->second.first; + + } else { + auto p = mapKeys.find(hdChain.masterKeyID); + if (p == mapKeys.end()) { + return false; + } + hdkey = p->second.GetPubKey(); + } + + //Make sure the version get's updated because older wallets may + //have the insecure mnemonic + meta.nVersion = CKeyMetadata::VERSION_WITH_SECURE_MNEMONIC; + + walletdb.WriteKeyMetadata(hdkey, meta); + return true; +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -791,6 +908,17 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) } pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + if(HasMnemonic()) { + if(!EncryptMnemonic(*pwalletdbEncryption, _vMasterKey)) + { + pwalletdbEncryption->TxnAbort(); + delete pwalletdbEncryption; + // We now probably have half of our keys encrypted in memory, and half not... + // die and let the user reload the unencrypted wallet. + assert(false); + } + } + if (!EncryptKeys(_vMasterKey)) { pwalletdbEncryption->TxnAbort(); @@ -1726,25 +1854,19 @@ CPubKey CWallet::GenerateNewHDMasterKey() return pubkey; } -CPubKey CWallet::GenerateMasterKeyFromMnemonic(const WordList& mnemonic, const std::string& passphrase) +CPubKey CWallet::SetMasterKeyMetadata( + const CExtKey& extkey, + const WordList& mnemonic, + const CPubKey& pubkey) { - CExtKey extkey; - std::array seed = mnemonic::MnemonicToSeed(mnemonic, passphrase); - extkey.SetMaster(seed.begin(), mnemonic::SEED_LENGTH); - - int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); - // calculate the pubkey - CPubKey pubkey = extkey.key.GetPubKey(); - assert(extkey.key.VerifyPubKey(pubkey)); - // set the hd keypath to "m" -> Master, refers the masterkeyid to itself metadata.hdKeypath = "m"; metadata.hdMasterKeyID = pubkey.GetID(); metadata.mnemonic = mnemonic::Unwords(mnemonic); - metadata.nVersion = CKeyMetadata::VERSION_WITH_MNEMONIC; + metadata.nVersion = CKeyMetadata::VERSION_WITH_SECURE_MNEMONIC; { LOCK(cs_wallet); @@ -1757,18 +1879,69 @@ CPubKey CWallet::GenerateMasterKeyFromMnemonic(const WordList& mnemonic, const s throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); } +} + +CPubKey CWallet::GenerateMasterKeyFromMnemonic( + const WordList& mnemonic, + const std::string& passphrase, + CExtKey& extkey) +{ + const auto seed = mnemonic::MnemonicToSeed(mnemonic, passphrase); + extkey.SetMaster(seed.begin(), mnemonic::SEED_LENGTH); +} + +CPubKey CWallet::GenerateMasterKeyFromMnemonic(const WordList& mnemonic, const std::string& passphrase) +{ + CExtKey extkey; + GenerateMasterKeyFromMnemonic(mnemonic, passphrase, extkey); + + // calculate the pubkey + CPubKey pubkey = extkey.key.GetPubKey(); + assert(extkey.key.VerifyPubKey(pubkey)); + + SetMasterKeyMetadata(extkey, mnemonic, pubkey); + return pubkey; } -bool CWallet::HasMnemonic() +bool CWallet::HasMnemonic() const { - return mapKeyMetadata[hdChain.masterKeyID].nVersion >= CKeyMetadata::VERSION_WITH_MNEMONIC; + auto p = mapKeyMetadata.find(hdChain.masterKeyID); + return p != mapKeyMetadata.end() && + p->second.nVersion >= CKeyMetadata::VERSION_WITH_MNEMONIC; } -std::string CWallet::GetMnemonic() +std::string CWallet::GetMnemonic() const { - if(HasMnemonic()) - return mapKeyMetadata[hdChain.masterKeyID].mnemonic; + if(HasMnemonic()){ + LOCK(cs_KeyStore); + auto p = mapKeyMetadata.find(hdChain.masterKeyID); + if(p == mapKeyMetadata.end()) { + return ""; + } + + if (!IsCrypted()) { + return p->second.mnemonic; + } + + if(CryptedWalletNeedsNewPassphrase()) { + return "old unencrypted mnemonics need to be encrypted"; + } + + if(IsLocked()) { + return "locked"; + } + + std::vector encrypted_mnemonic{ + p->second.mnemonic.begin(), p->second.mnemonic.end()}; + CKeyingMaterial decrypted_mnemonic; + auto master_key_id_string = hdChain.masterKeyID.ToString(); + auto master_key_id_hash = Hash(master_key_id_string.begin(), master_key_id_string.end()); + if(!DecryptSecret(encrypted_mnemonic, master_key_id_hash, decrypted_mnemonic)) { + return ""; + } + return {decrypted_mnemonic.begin(), decrypted_mnemonic.end()}; + } return ""; } @@ -5051,6 +5224,30 @@ bool CWallet::BackupWallet(const std::string& strDest) return dbw->Backup(strDest); } +bool CWallet::IsAValidMnemonic(const std::string& mnemonic) +{ + auto words = mnemonic::MnemonicStringToWords(mnemonic); + if(!mnemonic::IsAValidMnemonic(words)) { + return false; + } + + CKey secret; + std::string hdKeypath; + uint32_t hdchain_counter = 0; + DeriveNewBIP44ChildKey( + mnemonic, + hdKeypath, + secret, + hdchain_counter); + + auto master_pubkey = secret.GetPubKey(); + if(!AddressBeaconed(CMeritAddress{master_pubkey.GetID()})) { + return false; + } + + return true; +} + CKeyPool::CKeyPool() { nTime = GetTime(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9405fef50..e8def219d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -766,7 +766,11 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(CWalletDB& walletdb, CKeyMetadata& metadata, CKey& secret); - void DeriveNewBIP44ChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret); + void DeriveNewBIP44ChildKey( + const std::string& mnemonic, + std::string& hdKeypath, + CKey& secret, + uint32_t& hdchain_counter); std::set setKeyPool; int64_t m_max_keypool_index; @@ -942,6 +946,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface void UnlockCoin(const COutPoint& output); void UnlockAllCoins(); void ListLockedCoins(std::vector& vOutpts) const; + bool EncryptMnemonic(CWalletDB&, CKeyingMaterial& master_key); /* * Rescan abort properties @@ -955,6 +960,12 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface * Generate a new key */ CPubKey GenerateNewKey(CWalletDB& walletdb); + + //! Creates a new master key with the mnemonic phrase specified + bool ImportMnemonicAsMaster( + const std::string& mnemonic, + const std::string& passphrase = ""); + //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; bool AddKeyPubKeyWithDB(CWalletDB &walletdb,const CKey& key, const CPubKey &pubkey); @@ -998,6 +1009,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); + bool CryptedWalletNeedsNewPassphrase() const; void GetKeyBirthTimes(std::map &mapKeyBirth) const; unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; @@ -1225,8 +1237,8 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface void postInitProcess(CScheduler& scheduler); bool BackupWallet(const std::string& strDest); - bool HasMnemonic(); - std::string GetMnemonic(); + bool HasMnemonic() const; + std::string GetMnemonic() const; /* Set the HD chain model (chain child index counters) */ bool SetHDChain(const CHDChain& chain, bool memonly); @@ -1237,7 +1249,15 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /* Generates a new HD master key (will not be activated) */ CPubKey GenerateNewHDMasterKey(); - CPubKey GenerateMasterKeyFromMnemonic(const WordList& mnemonic, const std::string& passphrase = ""); + + CPubKey GenerateMasterKeyFromMnemonic( + const WordList& mnemonic, + const std::string& passphrase = ""); + + CPubKey GenerateMasterKeyFromMnemonic( + const WordList& mnemonic, + const std::string& passphrase, + CExtKey& extkey); /* Set the current HD master key (will reset the chain child index counters) Sets the master key's version based on the current wallet version (so the @@ -1303,6 +1323,14 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface bool IsConfirmed() const; referral::Address ReferralAddress() const; CPubKey ReferralPubKey() const; + + bool IsAValidMnemonic(const std::string& mnemonic); + +private: + CPubKey SetMasterKeyMetadata( + const CExtKey& extkey, + const WordList& menmonic, + const CPubKey& pubkey); }; /** A key allocated from the key pool. */ diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 1d6da9b14..1b667a93d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -58,9 +58,20 @@ bool CWalletDB::EraseTx(uint256 hash) return EraseIC(std::make_pair(std::string("tx"), hash)); } -bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) +bool CWalletDB::WriteKeyMetadata( + const CPubKey& vchPubKey, + const CKeyMetadata& keyMeta, + bool overwrite) { - if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) { + return WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, overwrite); +} + +bool CWalletDB::WriteKey( + const CPubKey& vchPubKey, + const CPrivKey& vchPrivKey, + const CKeyMetadata& keyMeta) +{ + if(!WriteKeyMetadata(vchPubKey, keyMeta, false)) { return false; } @@ -77,7 +88,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta) { - if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) { + if(!WriteKeyMetadata(vchPubKey, keyMeta)) { return false; } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 95994bf07..930d48fa0 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -102,7 +102,8 @@ class CKeyMetadata static const int VERSION_BASIC=1; static const int VERSION_WITH_HDDATA=10; static const int VERSION_WITH_MNEMONIC=11; - static const int CURRENT_VERSION=VERSION_WITH_HDDATA; + static const int VERSION_WITH_SECURE_MNEMONIC=12; + static const int CURRENT_VERSION = VERSION_WITH_SECURE_MNEMONIC; int nVersion; int64_t nCreateTime; // 0 means unknown std::string hdKeypath; //optional HD/bip32 keypath @@ -189,6 +190,7 @@ class CWalletDB bool WriteTx(const CWalletTx& wtx); bool EraseTx(uint256 hash); + bool WriteKeyMetadata(const CPubKey& vchPubKey, const CKeyMetadata& keyMeta, bool overwrite = true); bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta); bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta); bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey);