aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2011-08-24 22:07:26 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2011-08-31 14:19:43 +0200
commitb7bcaf940d27fa8cfe89422943fbeaab7a350930 (patch)
tree73f01425d448afe9f523a434420cfd09e32406e6
parent3f0816e3d926e0ea78ac7b6cd43efe62355885c8 (diff)
Wallet encryption part 2: ask passphrase when needed, add menu options
-rw-r--r--bitcoin-qt.pro9
-rw-r--r--doc/assets-attribution.txt5
-rw-r--r--src/qt/addresstablemodel.cpp8
-rw-r--r--src/qt/addresstablemodel.h7
-rw-r--r--src/qt/askpassphrasedialog.cpp186
-rw-r--r--src/qt/askpassphrasedialog.h40
-rw-r--r--src/qt/bitcoin.qrc1
-rw-r--r--src/qt/bitcoingui.cpp53
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/editaddressdialog.cpp5
-rw-r--r--src/qt/forms/askpassphrasedialog.ui148
-rw-r--r--src/qt/guiconstants.h3
-rw-r--r--src/qt/res/icons/key.pngbin0 -> 1239 bytes
-rw-r--r--src/qt/sendcoinsdialog.cpp9
-rw-r--r--src/qt/walletmodel.cpp76
-rw-r--r--src/qt/walletmodel.h36
16 files changed, 582 insertions, 9 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro
index e3dea66f10..28c5a33819 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 d498e8b4a8..91d2e65804 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 bd314ba0f0..6bda1e770c 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 296fa58054..bc505c48f0 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 0000000000..a297513a62
--- /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);
+}
diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h
new file mode 100644
index 0000000000..761612cbfd
--- /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
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index 1d5a58a4af..be0e4dce61 100644
--- 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>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6aa14dcf88..0c2eaab152 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 <QApplication>
#include <QMainWindow>
@@ -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 <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();
+ }
+}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 4b7131710c..484987ca68 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 2b3d9bf0f0..06e74db25a 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 0000000000..70d9180e75
--- /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>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index b7870199bb..0cb507501a 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 0000000000..757cad47ed
--- /dev/null
+++ b/src/qt/res/icons/key.png
Binary files differ
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index a9a89c282b..852d789805 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 <QMessageBox>
#include <QLocale>
@@ -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 9a7b56d33e..dfededca93 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 a585f8d8d6..b141c0762d 100644
--- 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 @@ 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<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 @@ 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);