diff --git a/components/PassphraseDialog.qml b/components/PassphraseDialog.qml
new file mode 100644
index 00000000..c7af29f9
--- /dev/null
+++ b/components/PassphraseDialog.qml
@@ -0,0 +1,327 @@
+// Copyright (c) 2014-2019, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import QtQuick 2.7
+import QtQuick.Controls 2.0
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.1
+import QtQuick.Controls.Styles 1.4
+import QtQuick.Window 2.0
+
+import "../components" as MoneroComponents
+
+Item {
+ id: root
+ visible: false
+ z: parent.z + 2
+
+ property bool isHidden: true
+ property alias passphrase: passphaseInput1.text
+ property string walletName
+ property string errorText
+
+ // same signals as Dialog has
+ signal accepted()
+ signal rejected()
+ signal closeCallback()
+
+ function open(walletName, errorText) {
+ inactiveOverlay.visible = true
+
+ root.walletName = walletName ? walletName : ""
+ root.errorText = errorText ? errorText : "";
+
+ leftPanel.enabled = false
+ middlePanel.enabled = false
+ titleBar.enabled = false
+ show();
+ root.visible = true;
+ passphaseInput1.text = "";
+ passphaseInput2.text = "";
+ passphaseInput1.focus = true
+ }
+
+ function close() {
+ inactiveOverlay.visible = false
+ leftPanel.enabled = true
+ middlePanel.enabled = true
+ titleBar.enabled = true
+ root.visible = false;
+ closeCallback();
+ }
+
+ function toggleIsHidden() {
+ passphaseInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
+ passphaseInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
+ isHidden = !isHidden;
+ }
+
+ function showError(errorText) {
+ open(root.walletName, errorText);
+ }
+
+ // TODO: implement without hardcoding sizes
+ width: 480
+ height: 360
+
+ // Make window draggable
+ MouseArea {
+ anchors.fill: parent
+ property point lastMousePos: Qt.point(0, 0)
+ onPressed: { lastMousePos = Qt.point(mouseX, mouseY); }
+ onMouseXChanged: root.x += (mouseX - lastMousePos.x)
+ onMouseYChanged: root.y += (mouseY - lastMousePos.y)
+ }
+
+ ColumnLayout {
+ z: inactiveOverlay.z + 1
+ id: mainLayout
+ spacing: 10
+ anchors { fill: parent; margins: 35 * scaleRatio }
+
+ ColumnLayout {
+ id: column
+
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignHCenter
+ Layout.maximumWidth: 400 * scaleRatio
+
+ Label {
+ text: root.walletName.length > 0 ? qsTr("Please enter wallet device passphrase for: ") + root.walletName : qsTr("Please enter wallet device passphrase")
+ Layout.fillWidth: true
+
+ font.pixelSize: 16 * scaleRatio
+ font.family: MoneroComponents.Style.fontLight.name
+
+ color: MoneroComponents.Style.defaultFontColor
+ }
+
+ Label {
+ text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.");
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+
+ font.pixelSize: 14 * scaleRatio
+ font.family: MoneroComponents.Style.fontLight.name
+
+ color: MoneroComponents.Style.warningColor
+ }
+
+ Label {
+ text: root.errorText
+ visible: root.errorText
+
+ color: MoneroComponents.Style.errorColor
+ font.pixelSize: 16 * scaleRatio
+ font.family: MoneroComponents.Style.fontLight.name
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ }
+
+ TextField {
+ id : passphaseInput1
+ Layout.topMargin: 6
+ Layout.fillWidth: true
+ horizontalAlignment: TextInput.AlignLeft
+ verticalAlignment: TextInput.AlignVCenter
+ font.family: MoneroComponents.Style.fontLight.name
+ font.pixelSize: 24 * scaleRatio
+ echoMode: TextInput.Password
+ bottomPadding: 10
+ leftPadding: 10
+ topPadding: 10
+ color: MoneroComponents.Style.defaultFontColor
+ selectionColor: MoneroComponents.Style.dimmedFontColor
+ selectedTextColor: MoneroComponents.Style.defaultFontColor
+ KeyNavigation.tab: passphaseInput2
+
+ background: Rectangle {
+ radius: 2
+ border.color: Qt.rgba(255, 255, 255, 0.35)
+ border.width: 1
+ color: "black"
+
+ Image {
+ width: 26 * scaleRatio
+ height: 26 * scaleRatio
+ opacity: 0.7
+ fillMode: Image.PreserveAspectFit
+ source: isHidden ? "../images/eyeShow.png" : "../images/eyeHide.png"
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 20
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ hoverEnabled: true
+ onClicked: {
+ toggleIsHidden()
+ }
+ onEntered: {
+ parent.opacity = 0.9
+ parent.width = 28 * scaleRatio
+ parent.height = 28 * scaleRatio
+ }
+ onExited: {
+ parent.opacity = 0.7
+ parent.width = 26 * scaleRatio
+ parent.height = 26 * scaleRatio
+ }
+ }
+ }
+ }
+
+ Keys.onEscapePressed: {
+ root.close()
+ root.rejected()
+ }
+ }
+
+ // padding
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignHCenter
+ height: 10
+ opacity: 0
+ color: "black"
+ }
+
+ Label {
+ text: qsTr("Please re-enter")
+ Layout.fillWidth: true
+
+ font.pixelSize: 16 * scaleRatio
+ font.family: MoneroComponents.Style.fontLight.name
+
+ color: MoneroComponents.Style.defaultFontColor
+ }
+
+ TextField {
+ id : passphaseInput2
+ Layout.topMargin: 6
+ Layout.fillWidth: true
+ horizontalAlignment: TextInput.AlignLeft
+ verticalAlignment: TextInput.AlignVCenter
+ font.family: MoneroComponents.Style.fontLight.name
+ font.pixelSize: 24 * scaleRatio
+ echoMode: TextInput.Password
+ KeyNavigation.tab: okButton
+ bottomPadding: 10
+ leftPadding: 10
+ topPadding: 10
+ color: MoneroComponents.Style.defaultFontColor
+ selectionColor: MoneroComponents.Style.dimmedFontColor
+ selectedTextColor: MoneroComponents.Style.defaultFontColor
+
+ background: Rectangle {
+ radius: 2
+ border.color: Qt.rgba(255, 255, 255, 0.35)
+ border.width: 1
+ color: "black"
+
+ Image {
+ width: 26 * scaleRatio
+ height: 26 * scaleRatio
+ opacity: 0.7
+ fillMode: Image.PreserveAspectFit
+ source: isHidden ? "../images/eyeShow.png" : "../images/eyeHide.png"
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 20
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ hoverEnabled: true
+ onClicked: {
+ toggleIsHidden()
+ }
+ onEntered: {
+ parent.opacity = 0.9
+ parent.width = 28 * scaleRatio
+ parent.height = 28 * scaleRatio
+ }
+ onExited: {
+ parent.opacity = 0.7
+ parent.width = 26 * scaleRatio
+ parent.height = 26 * scaleRatio
+ }
+ }
+ }
+ }
+
+ Keys.onReturnPressed: {
+ if (passphaseInput1.text === passphaseInput2.text) {
+ root.close()
+ root.accepted()
+ }
+ }
+ Keys.onEscapePressed: {
+ root.close()
+ root.rejected()
+ }
+ }
+
+ // padding
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignHCenter
+ height: 10
+ opacity: 0
+ color: "black"
+ }
+
+ // Ok/Cancel buttons
+ RowLayout {
+ id: buttons
+ spacing: 16 * scaleRatio
+ Layout.topMargin: 16
+ Layout.alignment: Qt.AlignRight
+
+ MoneroComponents.StandardButton {
+ id: cancelButton
+ text: qsTr("Cancel") + translationManager.emptyString
+ KeyNavigation.tab: passphaseInput1
+ onClicked: {
+ root.close()
+ root.rejected()
+ }
+ }
+ MoneroComponents.StandardButton {
+ id: okButton
+ text: qsTr("Continue")
+ KeyNavigation.tab: cancelButton
+ enabled: passphaseInput1.text === passphaseInput2.text
+ onClicked: {
+ root.close()
+ root.accepted()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/components/Style.qml b/components/Style.qml
index 52b49287..cc106239 100644
--- a/components/Style.qml
+++ b/components/Style.qml
@@ -17,6 +17,7 @@ QtObject {
property string defaultFontColor: "white"
property string dimmedFontColor: "#BBBBBB"
property string lightGreyFontColor: "#DFDFDF"
+ property string warningColor: "#963E00"
property string errorColor: "#FA6800"
property string inputBoxBackground: "black"
property string inputBoxBackgroundError: "#FFDDDD"
diff --git a/main.qml b/main.qml
index f460204b..d78f6343 100644
--- a/main.qml
+++ b/main.qml
@@ -85,6 +85,8 @@ ApplicationWindow {
property int disconnectedEpoch: 0
property int estimatedBlockchainSize: 75 // GB
property alias viewState: rootItem.state
+ property string prevSplashText;
+ property bool splashDisplayedBeforeButtonRequest;
property string remoteNodeService: {
// support user-defined remote node aggregators
@@ -266,6 +268,8 @@ ApplicationWindow {
wallet_path = moneroAccountsDir + wallet_path;
// console.log("opening wallet at: ", wallet_path, "with password: ", appWindow.walletPassword);
console.log("opening wallet at: ", wallet_path, ", network type: ", persistentSettings.nettype == NetworkType.MAINNET ? "mainnet" : persistentSettings.nettype == NetworkType.TESTNET ? "testnet" : "stagenet");
+
+ this.onWalletOpening();
walletManager.openWalletAsync(wallet_path, walletPassword,
persistentSettings.nettype, persistentSettings.kdfRounds);
}
@@ -286,6 +290,8 @@ ApplicationWindow {
currentWallet.unconfirmedMoneyReceived.disconnect(onWalletUnconfirmedMoneyReceived)
currentWallet.transactionCreated.disconnect(onTransactionCreated)
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
+ currentWallet.deviceButtonRequest.disconnect(onDeviceButtonRequest);
+ currentWallet.deviceButtonPressed.disconnect(onDeviceButtonPressed);
middlePanel.paymentClicked.disconnect(handlePayment);
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
middlePanel.getProofClicked.disconnect(handleGetProof);
@@ -341,6 +347,8 @@ ApplicationWindow {
currentWallet.unconfirmedMoneyReceived.connect(onWalletUnconfirmedMoneyReceived)
currentWallet.transactionCreated.connect(onTransactionCreated)
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
+ currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
+ currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
middlePanel.paymentClicked.connect(handlePayment);
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
middlePanel.getProofClicked.connect(handleGetProof);
@@ -422,7 +430,26 @@ ApplicationWindow {
}
}
+ function onDeviceButtonRequest(code){
+ prevSplashText = splash.messageText;
+ splashDisplayedBeforeButtonRequest = splash.visible;
+ appWindow.showProcessingSplash(qsTr("Please proceed to the device..."));
+ }
+
+ function onDeviceButtonPressed(){
+ if (splashDisplayedBeforeButtonRequest){
+ appWindow.showProcessingSplash(prevSplashText);
+ } else {
+ hideProcessingSplash();
+ }
+ }
+
+ function onWalletOpening(){
+ appWindow.showProcessingSplash(qsTr("Opening wallet ..."));
+ }
+
function onWalletOpened(wallet) {
+ hideProcessingSplash();
walletName = usefulName(wallet.path)
console.log(">>> wallet opened: " + wallet)
if (wallet.status !== Wallet.Status_Ok) {
@@ -470,9 +497,27 @@ ApplicationWindow {
}
function onWalletClosed(walletAddress) {
+ hideProcessingSplash();
console.log(">>> wallet closed: " + walletAddress)
}
+ function onWalletPassphraseNeeded(){
+ if(rootItem.state !== "normal") return;
+
+ hideProcessingSplash();
+
+ console.log(">>> wallet passphrase needed: ")
+ passphraseDialog.onAcceptedCallback = function() {
+ walletManager.onPassphraseEntered(passphraseDialog.passphrase);
+ this.onWalletOpening();
+ }
+ passphraseDialog.onRejectedCallback = function() {
+ walletManager.onPassphraseEntered("", true);
+ this.onWalletOpening();
+ }
+ passphraseDialog.open()
+ }
+
function onWalletUpdate() {
console.log(">>> wallet updated")
updateBalance();
@@ -1017,7 +1062,10 @@ ApplicationWindow {
//
walletManager.walletOpened.connect(onWalletOpened);
walletManager.walletClosed.connect(onWalletClosed);
+ walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
+ walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
+ walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
if(typeof daemonManager != "undefined") {
daemonManager.daemonStarted.connect(onDaemonStarted);
@@ -1245,6 +1293,23 @@ ApplicationWindow {
}
}
+ PassphraseDialog {
+ id: passphraseDialog
+ visible: false
+ z: parent.z + 1
+ anchors.fill: parent
+ property var onAcceptedCallback
+ property var onRejectedCallback
+ onAccepted: {
+ if (onAcceptedCallback)
+ onAcceptedCallback();
+ }
+ onRejected: {
+ if (onRejectedCallback)
+ onRejectedCallback();
+ }
+ }
+
PasswordDialog {
id: passwordDialog
visible: false
diff --git a/qml.qrc b/qml.qrc
index 61f23378..6f5088cb 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -117,6 +117,7 @@
pages/TxKey.qml
pages/SharedRingDB.qml
components/IconButton.qml
+ components/PassphraseDialog.qml
components/PasswordDialog.qml
components/NewPasswordDialog.qml
components/InputDialog.qml
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index e488b635..0c320842 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -75,6 +75,16 @@ public:
emit m_wallet->refreshed();
}
+ virtual void onDeviceButtonRequest(uint64_t code) override
+ {
+ emit m_wallet->deviceButtonRequest(code);
+ }
+
+ virtual void onDeviceButtonPressed() override
+ {
+ emit m_wallet->deviceButtonPressed();
+ }
+
private:
Wallet * m_wallet;
};
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index e3357528..2866c9fd 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -322,6 +322,8 @@ signals:
void newBlock(quint64 height, quint64 targetHeight);
void historyModelChanged() const;
void walletCreationHeightChanged();
+ void deviceButtonRequest(quint64 buttonCode);
+ void deviceButtonPressed();
// emitted when transaction is created async
void transactionCreated(PendingTransaction * transaction, QString address, QString paymentId, quint32 mixinCount);
diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp
index 6ac7e179..3ac59315 100644
--- a/src/libwalletqt/WalletManager.cpp
+++ b/src/libwalletqt/WalletManager.cpp
@@ -13,6 +13,57 @@
#include
#include
+class WalletPassphraseListenerImpl : public Monero::WalletListener
+{
+public:
+ WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_wallet(nullptr) {}
+
+ virtual void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
+ virtual void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
+ virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
+ virtual void newBlock(uint64_t height) override { (void) height; };
+ virtual void updated() override {};
+ virtual void refreshed() override {};
+
+ virtual Monero::optional onDevicePassphraseRequest(bool on_device) override
+ {
+ qDebug() << __FUNCTION__;
+ if (on_device) return Monero::optional();
+
+ m_mgr->onWalletPassphraseNeeded(m_wallet);
+
+ if (m_mgr->m_passphrase_abort)
+ {
+ throw std::runtime_error("Passphrase entry abort");
+ }
+
+ auto tmpPass = m_mgr->m_passphrase.toStdString();
+ m_mgr->m_passphrase = QString::null;
+
+ return Monero::optional(tmpPass);
+ }
+
+ virtual void onDeviceButtonRequest(uint64_t code) override
+ {
+ emit m_mgr->deviceButtonRequest(code);
+ }
+
+ virtual void onDeviceButtonPressed() override
+ {
+ emit m_mgr->deviceButtonPressed();
+ }
+
+ virtual void onSetWallet(Monero::Wallet * wallet) override
+ {
+ qDebug() << __FUNCTION__;
+ m_wallet = wallet;
+ }
+
+private:
+ Monero::Wallet * m_wallet;
+ WalletManager * m_mgr;
+};
+
WalletManager * WalletManager::m_instance = nullptr;
WalletManager *WalletManager::instance()
@@ -41,6 +92,8 @@ Wallet *WalletManager::createWallet(const QString &path, const QString &password
Wallet *WalletManager::openWallet(const QString &path, const QString &password, NetworkType::Type nettype, quint64 kdfRounds)
{
QMutexLocker locker(&m_mutex);
+ WalletPassphraseListenerImpl tmpListener(this);
+
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
delete m_currentWallet;
@@ -48,7 +101,9 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
qDebug("%s: opening wallet at %s, nettype = %d ",
__PRETTY_FUNCTION__, qPrintable(path), nettype);
- Monero::Wallet * w = m_pimpl->openWallet(path.toStdString(), password.toStdString(), static_cast(nettype), kdfRounds);
+ Monero::Wallet * w = m_pimpl->openWallet(path.toStdString(), password.toStdString(), static_cast(nettype), kdfRounds, &tmpListener);
+ w->setListener(nullptr);
+
qDebug("%s: opened wallet: %s, status: %d", __PRETTY_FUNCTION__, w->address(0, 0).c_str(), w->status());
m_currentWallet = new Wallet(w);
@@ -108,17 +163,48 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
const QString &deviceName, quint64 restoreHeight, const QString &subaddressLookahead)
{
QMutexLocker locker(&m_mutex);
+ WalletPassphraseListenerImpl tmpListener(this);
+
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
delete m_currentWallet;
m_currentWallet = NULL;
}
Monero::Wallet * w = m_pimpl->createWalletFromDevice(path.toStdString(), password.toStdString(), static_cast(nettype),
- deviceName.toStdString(), restoreHeight, subaddressLookahead.toStdString());
+ deviceName.toStdString(), restoreHeight, subaddressLookahead.toStdString(), 1, &tmpListener);
+ w->setListener(nullptr);
+
m_currentWallet = new Wallet(w);
+
+ // move wallet to the GUI thread. Otherwise it wont be emitting signals
+ if (m_currentWallet->thread() != qApp->thread()) {
+ m_currentWallet->moveToThread(qApp->thread());
+ }
+
return m_currentWallet;
}
+
+void WalletManager::createWalletFromDeviceAsync(const QString &path, const QString &password, NetworkType::Type nettype,
+ const QString &deviceName, quint64 restoreHeight, const QString &subaddressLookahead)
+{
+ auto lmbd = [=](){
+ return this->createWalletFromDevice(path, password, nettype, deviceName, restoreHeight, subaddressLookahead);
+ };
+
+ QFuture future = QtConcurrent::run(lmbd);
+
+ QFutureWatcher * watcher = new QFutureWatcher();
+
+ connect(watcher, &QFutureWatcher::finished,
+ this, [this, watcher]() {
+ QFuture future = watcher->future();
+ watcher->deleteLater();
+ emit walletCreated(future.result());
+ });
+ watcher->setFuture(future);
+}
+
QString WalletManager::closeWallet()
{
QMutexLocker locker(&m_mutex);
@@ -419,3 +505,23 @@ WalletManager::WalletManager(QObject *parent) : QObject(parent)
{
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
}
+
+void WalletManager::onWalletPassphraseNeeded(Monero::Wallet * wallet)
+{
+ m_mutex_pass.lock();
+ m_passphrase_abort = false;
+ emit this->walletPassphraseNeeded();
+
+ m_cond_pass.wait(&m_mutex_pass);
+ m_mutex_pass.unlock();
+}
+
+void WalletManager::onPassphraseEntered(const QString &passphrase, bool entry_abort)
+{
+ m_mutex_pass.lock();
+ m_passphrase = passphrase;
+ m_passphrase_abort = entry_abort;
+
+ m_cond_pass.wakeAll();
+ m_mutex_pass.unlock();
+}
diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h
index 5cefde41..44fc11bf 100644
--- a/src/libwalletqt/WalletManager.h
+++ b/src/libwalletqt/WalletManager.h
@@ -7,6 +7,8 @@
#include
#include
#include
+#include
+#include
#include "NetworkType.h"
class Wallet;
@@ -70,6 +72,13 @@ public:
const QString &deviceName,
quint64 restoreHeight = 0,
const QString &subaddressLookahead = "");
+
+ Q_INVOKABLE void createWalletFromDeviceAsync(const QString &path,
+ const QString &password,
+ NetworkType::Type nettype,
+ const QString &deviceName,
+ quint64 restoreHeight = 0,
+ const QString &subaddressLookahead = "");
/*!
* \brief closeWallet - closes current open wallet and frees memory
* \return wallet address
@@ -152,14 +161,22 @@ public:
// clear/rename wallet cache
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
+ Q_INVOKABLE void onWalletPassphraseNeeded(Monero::Wallet * wallet);
+ Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool entry_abort=false);
+
signals:
void walletOpened(Wallet * wallet);
+ void walletCreated(Wallet * wallet);
+ void walletPassphraseNeeded();
+ void deviceButtonRequest(quint64 buttonCode);
+ void deviceButtonPressed();
void walletClosed(const QString &walletAddress);
void checkUpdatesComplete(const QString &result) const;
public slots:
private:
+ friend class WalletPassphraseListenerImpl;
explicit WalletManager(QObject *parent = 0);
static WalletManager * m_instance;
@@ -167,6 +184,10 @@ private:
QMutex m_mutex;
QPointer m_currentWallet;
+ QWaitCondition m_cond_pass;
+ QMutex m_mutex_pass;
+ QString m_passphrase;
+ bool m_passphrase_abort;
};
#endif // WALLETMANAGER_H
diff --git a/wizard/WizardController.qml b/wizard/WizardController.qml
index 4224e904..e380aed9 100644
--- a/wizard/WizardController.qml
+++ b/wizard/WizardController.qml
@@ -44,6 +44,7 @@ Rectangle {
anchors.fill: parent
signal useMoneroClicked()
+ signal walletCreatedFromDevice(bool success)
function restart() {
wizardStateView.state = "wizardHome"
@@ -65,6 +66,7 @@ Rectangle {
wizardController.walletRestoreMode = 'seed'
wizardController.walletOptionsSubaddressLookahead = '';
wizardController.remoteNodes = {};
+ disconnect();
}
property var m_wallet;
@@ -366,6 +368,28 @@ Rectangle {
return success;
}
+ function disconnect(){
+ walletManager.walletCreated.disconnect(onWalletCreated);
+ walletManager.walletPassphraseNeeded.disconnect(onWalletPassphraseNeeded);
+ walletManager.deviceButtonRequest.disconnect(onDeviceButtonRequest);
+ walletManager.deviceButtonPressed.disconnect(onDeviceButtonPressed);
+ }
+
+ function connect(){
+ walletManager.walletCreated.connect(onWalletCreated);
+ walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
+ walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
+ walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
+ }
+
+ function deviceAttentionSplash(){
+ appWindow.showProcessingSplash(qsTr("Please proceed to the device..."));
+ }
+
+ function creatingWalletDeviceSplash(){
+ appWindow.showProcessingSplash(qsTr("Creating wallet from device..."));
+ }
+
function createWalletFromDevice() {
// TODO: create wallet in temporary filename and a) move it to the path specified by user after the final
// page submitted or b) delete it when program closed before reaching final page
@@ -376,30 +400,61 @@ Rectangle {
console.log("deleting wallet")
}
- var tmp_wallet_filename = oshelper.temporaryFilename();
- console.log("Creating temporary wallet", tmp_wallet_filename)
+ tmpWalletFilename = oshelper.temporaryFilename();
+ console.log("Creating temporary wallet", tmpWalletFilename)
var nettype = persistentSettings.nettype;
var restoreHeight = wizardController.walletOptionsRestoreHeight;
var subaddressLookahead = wizardController.walletOptionsSubaddressLookahead;
var deviceName = wizardController.walletOptionsDeviceName;
- var wallet = walletManager.createWalletFromDevice(tmp_wallet_filename, "", nettype, deviceName, restoreHeight, subaddressLookahead);
+ connect();
+ walletManager.createWalletFromDeviceAsync(tmpWalletFilename, "", nettype, deviceName, restoreHeight, subaddressLookahead);
+ creatingWalletDeviceSplash();
+ }
+
+ function onWalletCreated(wallet) {
+ splash.close()
var success = wallet.status === Wallet.Status_Ok;
if (success) {
wizardController.m_wallet = wallet;
wizardController.walletOptionsIsRecoveringFromDevice = true;
- wizardController.tmpWalletFilename = tmp_wallet_filename;
if (!wizardController.walletOptionsDeviceIsRestore) {
// User creates a hardware wallet for the first time. Use a recent block height from API.
wizardController.walletOptionsRestoreHeight = wizardController.m_wallet.walletCreationHeight;
}
} else {
console.log(wallet.errorString)
+ wizardController.tmpWalletFilename = '';
appWindow.showStatusMessage(qsTr(wallet.errorString), 5);
walletManager.closeWallet();
}
- return success;
+
+ disconnect();
+ walletCreatedFromDevice(success);
+ }
+
+ function onWalletPassphraseNeeded(){
+ splash.close()
+
+ console.log(">>> wallet passphrase needed: ");
+ passphraseDialog.onAcceptedCallback = function() {
+ walletManager.onPassphraseEntered(passphraseDialog.passphrase);
+ creatingWalletDeviceSplash();
+ }
+ passphraseDialog.onRejectedCallback = function() {
+ walletManager.onPassphraseEntered("", true);
+ creatingWalletDeviceSplash();
+ }
+ passphraseDialog.open()
+ }
+
+ function onDeviceButtonRequest(code){
+ deviceAttentionSplash();
+ }
+
+ function onDeviceButtonPressed(){
+ creatingWalletDeviceSplash();
}
function openWallet(){
diff --git a/wizard/WizardCreateDevice1.qml b/wizard/WizardCreateDevice1.qml
index 9ad2ca33..81130aa1 100644
--- a/wizard/WizardCreateDevice1.qml
+++ b/wizard/WizardCreateDevice1.qml
@@ -207,13 +207,9 @@ Rectangle {
}
wizardController.walletOptionsRestoreHeight = _restoreHeight;
}
- var written = wizardController.createWalletFromDevice();
- if(written){
- wizardController.walletOptionsIsRecoveringFromDevice = true;
- wizardStateView.state = "wizardCreateWallet2";
- } else {
- errorMsg.text = qsTr("Error writing wallet from hardware device. Check application logs.") + translationManager.emptyString;
- }
+
+ wizardController.walletCreatedFromDevice.connect(onCreateWalletFromDeviceCompleted);
+ wizardController.createWalletFromDevice();
}
}
}
@@ -230,4 +226,13 @@ Rectangle {
walletInput.reset();
}
}
+
+ function onCreateWalletFromDeviceCompleted(written){
+ if(written){
+ wizardStateView.state = "wizardCreateWallet2";
+ } else {
+ errorMsg.text = qsTr("Error writing wallet from hardware device. Check application logs.") + translationManager.emptyString;
+ }
+ wizardController.walletCreatedFromDevice.disconnect(onCreateWalletFromDeviceCompleted);
+ }
}