Mercurial > hg > bitcoin
changeset 1271:052a2a34f756 draft
Wallet encryption part 2: ask passphrase when needed, add menu options
author | Wladimir J. van der Laan <laanwj@gmail.com> |
---|---|
date | Wed, 24 Aug 2011 22:07:26 +0200 |
parents | 5785ebf92396 |
children | c4a6b003ac46 |
files | bitcoin-qt.pro doc/assets-attribution.txt src/qt/addresstablemodel.cpp src/qt/addresstablemodel.h src/qt/askpassphrasedialog.cpp src/qt/askpassphrasedialog.h src/qt/bitcoin.qrc src/qt/bitcoingui.cpp src/qt/bitcoingui.h src/qt/editaddressdialog.cpp src/qt/forms/askpassphrasedialog.ui src/qt/guiconstants.h src/qt/res/icons/key.png src/qt/sendcoinsdialog.cpp src/qt/walletmodel.cpp src/qt/walletmodel.h |
diffstat | 16 files changed, 582 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -90,7 +90,8 @@ src/qt/sendcoinsentry.h \ src/qt/qvalidatedlineedit.h \ src/qt/bitcoinunits.h \ - src/qt/qvaluecombobox.h + src/qt/qvaluecombobox.h \ + src/qt/askpassphrasedialog.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -134,7 +135,8 @@ src/qt/sendcoinsentry.cpp \ src/qt/qvalidatedlineedit.cpp \ src/qt/bitcoinunits.cpp \ - src/qt/qvaluecombobox.cpp + src/qt/qvaluecombobox.cpp \ + src/qt/askpassphrasedialog.cpp RESOURCES += \ src/qt/bitcoin.qrc @@ -146,7 +148,8 @@ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ src/qt/forms/overviewpage.ui \ - src/qt/forms/sendcoinsentry.ui + src/qt/forms/sendcoinsentry.ui \ + src/qt/forms/askpassphrasedialog.ui CODECFORTR = UTF-8 # for lrelease/lupdate
--- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -64,5 +64,10 @@ Site: https://bitcointalk.org/index.php?topic=32273.0 License: Public domain +Icon: src/qt/res/icons/key.png +Designer: VisualPharm (Ivan Boyko) +Icon Pack: Must Have +Site: http://findicons.com/icon/51009/key?id=51009 +License: Creative Commons Attribution (by)
--- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -267,6 +267,14 @@ else if(type == Receive) { // Generate a new address to associate with given label + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + editStatus = WALLET_UNLOCK_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(wallet->GetOrReuseKeyFromPool()).ToString(); } else
--- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -26,9 +26,10 @@ // Return status of last edit/insert operation enum EditStatus { - OK = 0, - INVALID_ADDRESS = 1, - DUPLICATE_ADDRESS = 2 + OK, + INVALID_ADDRESS, + DUPLICATE_ADDRESS, + WALLET_UNLOCK_FAILURE }; static const QString Send; /* Send addres */
new file mode 100644 --- /dev/null +++ b/src/qt/askpassphrasedialog.cpp @@ -0,0 +1,186 @@ +#include "askpassphrasedialog.h" +#include "ui_askpassphrasedialog.h" + +#include "guiconstants.h" +#include "walletmodel.h" + +#include <QMessageBox> +#include <QPushButton> + +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::AskPassphraseDialog), + mode(mode), + model(0) +{ + ui->setupUi(this); + ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); + + switch(mode) + { + case Encrypt: // Ask passphrase x2 + ui->passLabel1->hide(); + ui->passEdit1->hide(); + ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>.")); + setWindowTitle(tr("Encrypt wallet")); + break; + case Unlock: // Ask passphrase + ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet.")); + ui->passLabel2->hide(); + ui->passEdit2->hide(); + ui->passLabel3->hide(); + ui->passEdit3->hide(); + setWindowTitle(tr("Unlock wallet")); + break; + case Decrypt: // Ask passphrase + ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet.")); + ui->passLabel2->hide(); + ui->passEdit2->hide(); + ui->passLabel3->hide(); + ui->passEdit3->hide(); + setWindowTitle(tr("Decrypt wallet")); + break; + case ChangePass: // Ask old passphrase + new passphrase x2 + setWindowTitle(tr("Change passphrase")); + ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet.")); + break; + } + resize(minimumSize()); // Get rid of extra space in dialog + + textChanged(); + connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); +} + +AskPassphraseDialog::~AskPassphraseDialog() +{ + // Attempt to overwrite text so that they do not linger around in memory + ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size())); + ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size())); + ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size())); + delete ui; +} + +void AskPassphraseDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void AskPassphraseDialog::accept() +{ + std::string oldpass, newpass1, newpass2; + // TODO: mlock memory / munlock on return so they will not be swapped out, really need "mlockedstring" wrapper class to do this safely + oldpass.reserve(MAX_PASSPHRASE_SIZE); + newpass1.reserve(MAX_PASSPHRASE_SIZE); + newpass2.reserve(MAX_PASSPHRASE_SIZE); + oldpass.assign(ui->passEdit1->text().toStdString()); + newpass1.assign(ui->passEdit2->text().toStdString()); + newpass2.assign(ui->passEdit3->text().toStdString()); + + switch(mode) + { + case Encrypt: { + if(newpass1.empty() || newpass2.empty()) + { + // Cannot encrypt with empty passphrase + break; + } + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"), + tr("WARNING: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!\nAre you sure you wish to encrypt your wallet?"), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + if(retval == QMessageBox::Yes) + { + if(newpass1 == newpass2) + { + if(model->setWalletEncrypted(true, newpass1)) + { + QMessageBox::warning(this, tr("Wallet encrypted"), + tr("Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.")); + } + 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 + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The supplied passphrases do not match.")); + } + } + else + { + QDialog::reject(); // Cancelled + } + } break; + case Unlock: + if(!model->setWalletLocked(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet unlock failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case Decrypt: + if(!model->setWalletEncrypted(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet decryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case ChangePass: + if(newpass1 == newpass2) + { + if(model->changePassphrase(oldpass, newpass1)) + { + QMessageBox::information(this, tr("Wallet encrypted"), + tr("Wallet passphrase was succesfully changed.")); + QDialog::accept(); // Success + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The supplied passphrases do not match.")); + } + break; + } +} + +void AskPassphraseDialog::textChanged() +{ + // Validate input, set Ok button to enabled when accepable + bool acceptable = false; + switch(mode) + { + case Encrypt: // New passphrase x2 + acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + case Unlock: // Old passphrase x1 + case Decrypt: + acceptable = !ui->passEdit1->text().isEmpty(); + break; + case ChangePass: // Old passphrase x1, new passphrase x2 + acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable); +}
new file mode 100644 --- /dev/null +++ b/src/qt/askpassphrasedialog.h @@ -0,0 +1,40 @@ +#ifndef ASKPASSPHRASEDIALOG_H +#define ASKPASSPHRASEDIALOG_H + +#include <QDialog> + +namespace Ui { + class AskPassphraseDialog; +} + +class WalletModel; + +class AskPassphraseDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + Encrypt, // Ask passphrase x2 + Unlock, // Ask passphrase + ChangePass, // Ask old passphrase + new passphrase x2 + Decrypt // Ask passphrase + }; + + explicit AskPassphraseDialog(Mode mode, QWidget *parent = 0); + ~AskPassphraseDialog(); + + void accept(); + + void setModel(WalletModel *model); + +private: + Ui::AskPassphraseDialog *ui; + Mode mode; + WalletModel *model; + +private slots: + void textChanged(); +}; + +#endif // ASKPASSPHRASEDIALOG_H
--- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -36,6 +36,7 @@ <file alias="tx_inout">res/icons/tx_inout.png</file> <file alias="lock_closed">res/icons/lock_closed.png</file> <file alias="lock_open">res/icons/lock_open.png</file> + <file alias="key">res/icons/key.png</file> </qresource> <qresource prefix="/images"> <file alias="about">res/images/about.png</file>
--- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -19,6 +19,7 @@ #include "overviewpage.h" #include "bitcoinunits.h" #include "guiconstants.h" +#include "askpassphrasedialog.h" #include <QApplication> #include <QMainWindow> @@ -48,6 +49,8 @@ QMainWindow(parent), clientModel(0), walletModel(0), + encryptWalletAction(0), + changePassphraseAction(0), trayIcon(0) { resize(850, 550); @@ -66,6 +69,9 @@ file->addAction(quitAction); QMenu *settings = menuBar()->addMenu(tr("&Settings")); + settings->addAction(encryptWalletAction); + settings->addAction(changePassphraseAction); + settings->addSeparator(); settings->addAction(optionsAction); QMenu *help = menuBar()->addMenu(tr("&Help")); @@ -199,11 +205,18 @@ openBitcoinAction->setToolTip(tr("Show the Bitcoin window")); exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); exportAction->setToolTip(tr("Export the current view to a file")); + encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet"), this); + encryptWalletAction->setToolTip(tr("Encrypt or decrypt wallet")); + encryptWalletAction->setCheckable(true); + changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase"), this); + changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(show())); + connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool))); + connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase())); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -254,6 +267,9 @@ // Balloon popup for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(incomingTransaction(QModelIndex,int,int))); + + // Ask for passphrase if needed + connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); } void BitcoinGUI::createTrayIcon() @@ -544,16 +560,53 @@ { case WalletModel::Unencrypted: labelEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: labelEncryptionIcon->show(); labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::Locked: labelEncryptionIcon->show(); labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; } } + +void BitcoinGUI::encryptWallet(bool status) +{ + AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt: + AskPassphraseDialog::Decrypt, this); + dlg.setModel(walletModel); + dlg.exec(); + + setEncryptionStatus(walletModel->getEncryptionStatus()); +} + +void BitcoinGUI::changePassphrase() +{ + AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this); + dlg.setModel(walletModel); + dlg.exec(); +} + +void BitcoinGUI::unlockWallet() +{ + // Unlock wallet if needed + if(walletModel->getEncryptionStatus() == WalletModel::Locked) + { + AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); + dlg.setModel(walletModel); + dlg.exec(); + } +}
--- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -73,6 +73,8 @@ QAction *optionsAction; QAction *openBitcoinAction; QAction *exportAction; + QAction *encryptWalletAction; + QAction *changePassphraseAction; QSystemTrayIcon *trayIcon; TransactionView *transactionView; @@ -108,6 +110,9 @@ void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void incomingTransaction(const QModelIndex & parent, int start, int end); + void encryptWallet(bool status); + void changePassphrase(); + void unlockWallet(); }; #endif
--- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -92,6 +92,11 @@ tr("The entered address \"%1\" is not a valid bitcoin address.").arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); return; + case AddressTableModel::WALLET_UNLOCK_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("Could not unlock wallet."), + QMessageBox::Ok, QMessageBox::Ok); + return; } return;
new file mode 100644 --- /dev/null +++ b/src/qt/forms/askpassphrasedialog.ui @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AskPassphraseDialog</class> + <widget class="QDialog" name="AskPassphraseDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>589</width> + <height>228</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>550</width> + <height>0</height> + </size> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="warningLabel"> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + </widget> + </item> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <item row="1" column="0"> + <widget class="QLabel" name="passLabel1"> + <property name="text"> + <string>Enter passphrase</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="passEdit1"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="passLabel2"> + <property name="text"> + <string>New passphrase</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="passEdit2"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="passLabel3"> + <property name="text"> + <string>Repeat new passphrase</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="passEdit3"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AskPassphraseDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AskPassphraseDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -4,6 +4,9 @@ /* Milliseconds between model updates */ static const int MODEL_UPDATE_DELAY = 500; +/* Maximum passphrase length */ +static const int MAX_PASSPHRASE_SIZE = 1024; + /* Size of icons in status bar */ static const int STATUSBAR_ICONSIZE = 16;
new file mode 100644 index 0000000000000000000000000000000000000000..757cad47ed519f9e9040c92759150989d03e67b4 GIT binary patch literal 1239 zc$@*(1StE7P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F8000D@Nkl<ZScScp zOKenC7{`C-&Ye4*>4UaRVv2|~@qu815EG1qurN^sV}L}WF)rA!W1)!~MZ!XZL_`t{ zD|Ca=#2}zV46no}p=yO{O>OB5Xn}Uhv@`9rGjm_(b8&}4Yai26ILVhgH}~H2|9$6t zk26X{m})4c#@u)AjBgE>S8O!P%Bn<A07Ex!G`1xC&o>@Qw*fvd>7?WJRIvc9JLcDx zS5+OWdi=@dk(ye>tU{-|3B_yC1FdAPv~#(!ZRe_e1D^tUKu;%LG{Ci8@yb|r{7UuG zXDdCw1iPsboxBDjAYd(ffyhG!Zn~FC7a9&czkgr{FgzY&A}wP|^};Z%lH=727gyT0 zCV%xj+DwdeS;pB0SH2ps=*YF4W4P`QOi?_&bn*5>Z&bVr#DLNLo6J#jI`NJV;w$D= z$JgrE!#Ih0a=C2h`hC6cHgq{Xx4qoGp>);O73zyn(xyCK#mYsoZ7}pR;1q0f=caXE zu_+pk;J7Km_?&9a9)EdRq-6O5Gq<op4cU%%n|s`IXG({+uP?uTAX-+#Bh|4rKqWAE z?*PJL<!L13qDxn@Zgu*dm!9(w?I6B~_zvjcD)}th040ho&sdLr`<K&0unm(2+Oz-y z%5?-I62)4wj!<PSIB9SP$6mFIlj=ckT*vi<c+98*!uJf|Y3a&jH07akr1unlrgoI< z4rK)KJQNO2zJy%P=ir5W+jMgm1<*gDe=z-#_l*7!^J;ByI)dbqv8DiBQbS++6*`ic z-vF;*0zo>@Dh*ffPhIqk!MfDnE!eUERbipZEmWz6idv|Wg2gIHT<pd1(wu6|en*fY zHd8ErQ2>Bn-<{hQwYoEY(@n&90u>9RVkWB0M3tKOW|V=3n?#3O_&1UKe(x{&I)V(a z@1AIUSuYkq-a0&Z`AkdyrsT~_t#*48cqwobAQ><{vZ)@D&2^k?==^%~;la;<TR<9c z$4{n|8Us>nFHk@PsBZpb$%Q2wmOhG;0YgE^U?h=c@9z2!cb&<d1`-7uCTL6(dsWb# zz77=LCs@v;Gn{igvG4|c2pia`H2GT@x>9~O&<FG~K_8fG6x9pvf^d`|XoHT^?PU9H z^yO~qPP8|E`KQwa3;`p>iq8~al)`sy2uDyV=!S-(lwgJInTXFCK$(C76*dq7B}hSd z%EQRq00^K$C<TTQ1XJFZ<V=NU4G>g41C&A;2Ho8wxt-_r9)eSlci#cD0EUSIlrj+w zra_({0PU&2o&x|dP%2XRX;D*C68&gR=;Ok}|ChSC2G9U16nv^I97p2|22cT5_Z<MO zLGBc|tJia#n~%D0^T2{o3+ODM(O#am9=DNT|5H4O7|^~(YzMTU{2VJ5g*P62G4wh? z<C#$rniYT$?0^VJ&ci@hSf*YfIE3E+7;y7;pY}XNKx-kF_4wzOoCKm2-`r*oaOC&Y zYbW;n_zfzAuQe@w`t19^`p1AQGblu(=K>L#(k2)tF#qV5$gv}Dmi+{*1)c)t&Bmdk z|D28+A%eWGF5C|gWWuBekhuo94RJf@oY{y2{0A`aP-hVaBPRd=002ovPDHLkV1k(u BMR))J
--- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -6,6 +6,7 @@ #include "optionsmodel.h" #include "sendcoinsentry.h" #include "guiutil.h" +#include "askpassphrasedialog.h" #include <QMessageBox> #include <QLocale> @@ -84,6 +85,13 @@ return; } + WalletModel::UnlockContext ctx(model->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet was cancelled + return; + } + WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients); switch(sendstatus.status) { @@ -118,7 +126,6 @@ tr("Error: Transaction creation failed "), QMessageBox::Ok, QMessageBox::Ok); break; - break; case WalletModel::TransactionCommitFailed: QMessageBox::warning(this, tr("Send Coins"), tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."),
--- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -199,3 +199,79 @@ return Unlocked; } } + +bool WalletModel::setWalletEncrypted(bool encrypted, const std::string &passphrase) +{ + if(encrypted) + { + // Encrypt + return wallet->EncryptWallet(passphrase); + } + else + { + // Decrypt -- TODO; not supported yet + return false; + } +} + +bool WalletModel::setWalletLocked(bool locked, const std::string &passPhrase) +{ + if(locked) + { + // Lock + return wallet->Lock(); + } + else + { + // Unlock + return wallet->Unlock(passPhrase); + } +} + +bool WalletModel::changePassphrase(const std::string &oldPass, const std::string &newPass) +{ + bool retval; + CRITICAL_BLOCK(wallet->cs_vMasterKey) + { + wallet->Lock(); // Make sure wallet is locked before attempting pass change + retval = wallet->ChangeWalletPassphrase(oldPass, newPass); + } + return retval; +} + +// WalletModel::UnlockContext implementation +WalletModel::UnlockContext WalletModel::requestUnlock() +{ + bool was_locked = getEncryptionStatus() == Locked; + if(was_locked) + { + // Request UI to unlock wallet + emit requireUnlock(); + } + // If wallet is still locked, unlock was failed or cancelled, mark context as invalid + bool valid = getEncryptionStatus() != Locked; + + return UnlockContext(this, valid, was_locked); +} + +WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock): + wallet(wallet), + valid(valid), + relock(relock) +{ +} + +WalletModel::UnlockContext::~UnlockContext() +{ + if(valid && relock) + { + wallet->setWalletLocked(true); + } +} + +void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) +{ + // Transfer context; old object no longer relocks wallet + *this = rhs; + rhs.relock = false; +}
--- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -2,6 +2,7 @@ #define WALLETMODEL_H #include <QObject> +#include <string> class OptionsModel; class AddressTableModel; @@ -52,8 +53,6 @@ int getNumTransactions() const; EncryptionStatus getEncryptionStatus() const; - bool isEncrypted() const; - // Check address for validity bool validateAddress(const QString &address); @@ -71,6 +70,38 @@ // Send coins to a list of recipients SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients); + + // Wallet encryption + bool setWalletEncrypted(bool encrypted, const std::string &passphrase); + // Passphrase only needed when unlocking + bool setWalletLocked(bool locked, const std::string &passPhrase=std::string()); + bool changePassphrase(const std::string &oldPass, const std::string &newPass); + + // RAI object for unlocking wallet, returned by requestUnlock() + class UnlockContext + { + public: + UnlockContext(WalletModel *wallet, bool valid, bool relock); + ~UnlockContext(); + + bool isValid() const { return valid; } + + UnlockContext(const UnlockContext& obj) + { CopyFrom(obj); } + private: + UnlockContext& operator=(const UnlockContext& rhs) + { CopyFrom(rhs); return *this; } + + private: + WalletModel *wallet; + bool valid; + mutable bool relock; // mutable, as it can be set to false by copying + + void CopyFrom(const UnlockContext& rhs); + }; + + UnlockContext requestUnlock(); + private: CWallet *wallet; @@ -90,6 +121,7 @@ void balanceChanged(qint64 balance, qint64 unconfirmedBalance); void numTransactionsChanged(int count); void encryptionStatusChanged(int status); + void requireUnlock(); // Asynchronous error notification void error(const QString &title, const QString &message);