From: Wladimir J. van der Laan Date: Wed, 24 Aug 2011 20:07:26 +0000 (+0200) Subject: Wallet encryption part 2: ask passphrase when needed, add menu options X-Git-Tag: v0.4.0-unstable~226^2~57^2~45 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=b7bcaf940d27fa8cfe89422943fbeaab7a350930 Wallet encryption part 2: ask passphrase when needed, add menu options --- diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index e3dea66..28c5a33 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -90,7 +90,8 @@ HEADERS += src/qt/bitcoingui.h \ 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 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ 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 @@ FORMS += \ 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 diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index d498e8b..91d2e65 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -64,5 +64,10 @@ Designer: Crobbo (forum) 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) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index bd314ba..6bda1e7 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -267,6 +267,14 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con 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 diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 296fa58..bc505c4 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -26,9 +26,10 @@ public: // 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 */ diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp new file mode 100644 index 0000000..a297513 --- /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 +#include + +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.
Please use a passphrase of 10 or more random characters, or eight or more words.")); + 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 LOSE ALL OF YOUR BITCOINS!\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); +} diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h new file mode 100644 index 0000000..761612c --- /dev/null +++ b/src/qt/askpassphrasedialog.h @@ -0,0 +1,40 @@ +#ifndef ASKPASSPHRASEDIALOG_H +#define ASKPASSPHRASEDIALOG_H + +#include + +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 diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 1d5a58a..be0e4dc 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -36,6 +36,7 @@ res/icons/tx_inout.png res/icons/lock_closed.png res/icons/lock_open.png + res/icons/key.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6aa14dc..0c2eaab 100644 --- 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 #include @@ -48,6 +49,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QMainWindow(parent), clientModel(0), walletModel(0), + encryptWalletAction(0), + changePassphraseAction(0), trayIcon(0) { resize(850, 550); @@ -66,6 +69,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): 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 @@ void BitcoinGUI::createActions() 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 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) // 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 @@ void BitcoinGUI::setEncryptionStatus(int status) { 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 encrypted and currently unlocked")); + 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 encrypted and currently locked")); + 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(); + } +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 4b71317..484987c 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -73,6 +73,8 @@ private: QAction *optionsAction; QAction *openBitcoinAction; QAction *exportAction; + QAction *encryptWalletAction; + QAction *changePassphraseAction; QSystemTrayIcon *trayIcon; TransactionView *transactionView; @@ -108,6 +110,9 @@ private slots: 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 diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 2b3d9bf..06e74db 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -92,6 +92,11 @@ void EditAddressDialog::accept() 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; diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui new file mode 100644 index 0000000..70d9180 --- /dev/null +++ b/src/qt/forms/askpassphrasedialog.ui @@ -0,0 +1,148 @@ + + + AskPassphraseDialog + + + + 0 + 0 + 589 + 228 + + + + + 0 + 0 + + + + + 550 + 0 + + + + Dialog + + + + + + TextLabel + + + Qt::RichText + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Enter passphrase + + + + + + + QLineEdit::Password + + + + + + + New passphrase + + + + + + + QLineEdit::Password + + + + + + + Repeat new passphrase + + + + + + + QLineEdit::Password + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AskPassphraseDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AskPassphraseDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index b787019..0cb5075 100644 --- 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; diff --git a/src/qt/res/icons/key.png b/src/qt/res/icons/key.png new file mode 100644 index 0000000..757cad4 Binary files /dev/null and b/src/qt/res/icons/key.png differ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a9a89c2..852d789 100644 --- 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 #include @@ -84,6 +85,13 @@ void SendCoinsDialog::on_sendButton_clicked() 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 @@ void SendCoinsDialog::on_sendButton_clicked() 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."), diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 9a7b56d..dfededc 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -199,3 +199,79 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const 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; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index a585f8d..b141c07 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -2,6 +2,7 @@ #define WALLETMODEL_H #include +#include class OptionsModel; class AddressTableModel; @@ -52,8 +53,6 @@ public: int getNumTransactions() const; EncryptionStatus getEncryptionStatus() const; - bool isEncrypted() const; - // Check address for validity bool validateAddress(const QString &address); @@ -71,6 +70,38 @@ public: // Send coins to a list of recipients SendCoinsReturn sendCoins(const QList &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 @@ signals: 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);