diff --git a/components/DevicePassphraseDialog.qml b/components/DevicePassphraseDialog.qml
new file mode 100644
index 00000000..4bbe2fab
--- /dev/null
+++ b/components/DevicePassphraseDialog.qml
@@ -0,0 +1,104 @@
+// Copyright (c) 2014-2020, 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.9
+import "." as MoneroComponents
+
+Item {
+ id: root
+
+ property var onAcceptedCallback
+ property var onWalletEntryCallback
+ property var onRejectedCallback
+
+ function open(canEnterOnDevice_) {
+ var canEnterOnDevice = canEnterOnDevice_ !== null ? canEnterOnDevice_ : canEnterOnDevice
+ root.visible = true;
+
+ if (canEnterOnDevice) {
+ entryChooserDialog.okText = qsTr("Hardware wallet")
+ entryChooserDialog.cancelText = qsTr("Computer")
+ entryChooserDialog.open()
+ } else {
+ openPassphraseDialog()
+ }
+ }
+
+ function openPassphraseDialog() {
+ root.visible = true
+ passphraseDialog.openPassphraseDialog()
+ }
+
+ function close() {
+ root.visible = false;
+ if (entryChooserDialog.visible)
+ entryChooserDialog.close()
+ if (passphraseDialog.visible)
+ passphraseDialog.close()
+ }
+
+ StandardDialog {
+ id: entryChooserDialog
+ title: qsTr("Hardware wallet passphrase") + translationManager.emptyString
+ text: qsTr("Please select where you want to enter passphrase.\nIt is recommended to enter passphrase on the hardware wallet for better security.") + translationManager.emptyString
+
+ onAccepted: {
+ if (onWalletEntryCallback){
+ onWalletEntryCallback()
+ }
+ }
+
+ onRejected: {
+ openPassphraseDialog()
+ }
+
+ onCloseCallback: {
+ root.close()
+ }
+ }
+
+ PasswordDialog {
+ id: passphraseDialog
+ anchors.fill: parent
+ passphraseDialogMode: true
+
+ onAcceptedPassphrase: {
+ if (onAcceptedCallback)
+ onAcceptedCallback(passphraseDialog.password);
+ }
+
+ onRejectedPassphrase: {
+ if (onRejectedCallback)
+ onRejectedCallback();
+ }
+
+ onCloseCallback: {
+ root.close()
+ }
+ }
+}
diff --git a/main.qml b/main.qml
index ace2064a..bbb6f2fa 100644
--- a/main.qml
+++ b/main.qml
@@ -293,6 +293,7 @@ ApplicationWindow {
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
currentWallet.deviceButtonRequest.disconnect(onDeviceButtonRequest);
currentWallet.deviceButtonPressed.disconnect(onDeviceButtonPressed);
+ currentWallet.walletPassphraseNeeded.disconnect(onWalletPassphraseNeededWallet);
currentWallet.transactionCommitted.disconnect(onTransactionCommitted);
middlePanel.paymentClicked.disconnect(handlePayment);
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
@@ -360,6 +361,7 @@ ApplicationWindow {
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
+ currentWallet.walletPassphraseNeeded.connect(onWalletPassphraseNeededWallet);
currentWallet.transactionCommitted.connect(onTransactionCommitted);
middlePanel.paymentClicked.connect(handlePayment);
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
@@ -558,19 +560,32 @@ ApplicationWindow {
}
}
- function onWalletPassphraseNeeded(){
+ function onWalletPassphraseNeededManager(on_device){
+ onWalletPassphraseNeeded(walletManager, on_device)
+ }
+
+ function onWalletPassphraseNeededWallet(on_device){
+ onWalletPassphraseNeeded(currentWallet, on_device)
+ }
+
+ function onWalletPassphraseNeeded(handler, on_device){
hideProcessingSplash();
console.log(">>> wallet passphrase needed: ")
- passwordDialog.onAcceptedPassphraseCallback = function() {
- walletManager.onPassphraseEntered(passwordDialog.password);
+ devicePassphraseDialog.onAcceptedCallback = function(passphrase) {
+ handler.onPassphraseEntered(passphrase, false, false);
appWindow.onWalletOpening();
}
- passwordDialog.onRejectedPassphraseCallback = function() {
- walletManager.onPassphraseEntered("", true);
+ devicePassphraseDialog.onWalletEntryCallback = function() {
+ handler.onPassphraseEntered("", true, false);
appWindow.onWalletOpening();
}
- passwordDialog.openPassphraseDialog()
+ devicePassphraseDialog.onRejectedCallback = function() {
+ handler.onPassphraseEntered("", false, true);
+ appWindow.onWalletOpening();
+ }
+
+ devicePassphraseDialog.open(on_device)
}
function onWalletUpdate() {
@@ -1295,7 +1310,7 @@ ApplicationWindow {
walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
- walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
+ walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeededManager);
IPC.uriHandler.connect(onUriHandler);
if(typeof daemonManager != "undefined") {
@@ -1512,8 +1527,6 @@ ApplicationWindow {
anchors.fill: parent
property var onAcceptedCallback
property var onRejectedCallback
- property var onAcceptedPassphraseCallback
- property var onRejectedPassphraseCallback
onAccepted: {
if (onAcceptedCallback)
onAcceptedCallback();
@@ -1537,14 +1550,13 @@ ApplicationWindow {
informationPopup.open();
}
onRejectedNewPassword: {}
- onAcceptedPassphrase: {
- if (onAcceptedPassphraseCallback)
- onAcceptedPassphraseCallback();
- }
- onRejectedPassphrase: {
- if (onRejectedPassphraseCallback)
- onRejectedPassphraseCallback();
- }
+ }
+
+ DevicePassphraseDialog {
+ id: devicePassphraseDialog
+ visible: false
+ z: parent.z + 1
+ anchors.fill: parent
}
InputDialog {
@@ -1708,7 +1720,7 @@ ApplicationWindow {
anchors.fill: blurredArea
source: blurredArea
radius: 64
- visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible
+ visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible
}
diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro
index 68158bbb..c2a9ea4a 100644
--- a/monero-wallet-gui.pro
+++ b/monero-wallet-gui.pro
@@ -53,6 +53,7 @@ HEADERS += \
src/main/oscursor.h \
src/libwalletqt/WalletManager.h \
src/libwalletqt/Wallet.h \
+ src/libwalletqt/PassphraseHelper.h \
src/libwalletqt/PendingTransaction.h \
src/libwalletqt/TransactionHistory.h \
src/libwalletqt/TransactionInfo.h \
@@ -91,7 +92,9 @@ SOURCES += src/main/main.cpp \
src/main/clipboardAdapter.cpp \
src/main/oscursor.cpp \
src/libwalletqt/WalletManager.cpp \
+ src/libwalletqt/WalletListenerImpl.cpp \
src/libwalletqt/Wallet.cpp \
+ src/libwalletqt/PassphraseHelper.cpp \
src/libwalletqt/PendingTransaction.cpp \
src/libwalletqt/TransactionHistory.cpp \
src/libwalletqt/TransactionInfo.cpp \
diff --git a/qml.qrc b/qml.qrc
index eb24cc20..84e1c1cc 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -99,6 +99,7 @@
components/ProcessingSplash.qml
components/ProgressBar.qml
components/StandardDialog.qml
+ components/DevicePassphraseDialog.qml
pages/Sign.qml
components/DaemonManagerDialog.qml
version.js
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index de84824b..3c45c5c4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -15,7 +15,9 @@ file(GLOB SOURCE_FILES
"main/*.h"
"main/*.cpp"
"libwalletqt/WalletManager.cpp"
+ "libwalletqt/WalletListenerImpl.cpp"
"libwalletqt/Wallet.cpp"
+ "libwalletqt/PassphraseHelper.cpp"
"libwalletqt/PendingTransaction.cpp"
"libwalletqt/TransactionHistory.cpp"
"libwalletqt/TransactionInfo.cpp"
@@ -29,6 +31,7 @@ file(GLOB SOURCE_FILES
"libwalletqt/UnsignedTransaction.cpp"
"libwalletqt/WalletManager.h"
"libwalletqt/Wallet.h"
+ "libwalletqt/PassphraseHelper.h"
"libwalletqt/PendingTransaction.h"
"libwalletqt/TransactionHistory.h"
"libwalletqt/TransactionInfo.h"
diff --git a/src/libwalletqt/PassphraseHelper.cpp b/src/libwalletqt/PassphraseHelper.cpp
new file mode 100644
index 00000000..9e851f1b
--- /dev/null
+++ b/src/libwalletqt/PassphraseHelper.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2014-2020, 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.
+
+#include "PassphraseHelper.h"
+#include
+#include
+
+Monero::optional PassphraseHelper::onDevicePassphraseRequest(bool & on_device)
+{
+ qDebug() << __FUNCTION__;
+ QMutexLocker locker(&m_mutex_pass);
+ m_passphrase_on_device = true;
+ m_passphrase_abort = false;
+
+ if (m_prompter != nullptr){
+ m_prompter->onWalletPassphraseNeeded(on_device);
+ }
+
+ m_cond_pass.wait(&m_mutex_pass);
+
+ if (m_passphrase_abort)
+ {
+ throw std::runtime_error("Passphrase entry abort");
+ }
+
+ on_device = m_passphrase_on_device;
+ if (!on_device) {
+ auto tmpPass = m_passphrase.toStdString();
+ m_passphrase = QString();
+ return Monero::optional(tmpPass);
+ } else {
+ return Monero::optional();
+ }
+}
+
+void PassphraseHelper::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
+{
+ qDebug() << __FUNCTION__;
+ QMutexLocker locker(&m_mutex_pass);
+ m_passphrase = passphrase;
+ m_passphrase_abort = entry_abort;
+ m_passphrase_on_device = enter_on_device;
+
+ m_cond_pass.wakeAll();
+}
diff --git a/src/libwalletqt/PassphraseHelper.h b/src/libwalletqt/PassphraseHelper.h
new file mode 100644
index 00000000..03274992
--- /dev/null
+++ b/src/libwalletqt/PassphraseHelper.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2014-2020, 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.
+
+#ifndef MONERO_GUI_PASSPHRASEHELPER_H
+#define MONERO_GUI_PASSPHRASEHELPER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * Implements component responsible for showing entry prompt to the user,
+ * typically Wallet / Wallet manager.
+ */
+class PassprasePrompter {
+public:
+ virtual void onWalletPassphraseNeeded(bool onDevice) = 0;
+};
+
+/**
+ * Implements receiver of the passphrase responsible for passing it back to the wallet,
+ * typically wallet listener.
+ */
+class PassphraseReceiver {
+public:
+ virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) = 0;
+};
+
+class PassphraseHelper {
+public:
+ PassphraseHelper(PassprasePrompter * prompter=nullptr): m_prompter(prompter) {};
+ PassphraseHelper(const PassphraseHelper & h): PassphraseHelper(h.m_prompter) {};
+ Monero::optional onDevicePassphraseRequest(bool & on_device);
+ void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort);
+
+private:
+ PassprasePrompter * m_prompter;
+ QWaitCondition m_cond_pass;
+ QMutex m_mutex_pass;
+ QString m_passphrase;
+ bool m_passphrase_abort;
+ bool m_passphrase_on_device;
+
+};
+
+#endif //MONERO_GUI_PASSPHRASEHELPER_H
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index 355ce6a9..2d7f6722 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -58,66 +58,6 @@ namespace {
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
}
-class WalletListenerImpl : public Monero::WalletListener
-{
-public:
- WalletListenerImpl(Wallet * w)
- : m_wallet(w)
- {
-
- }
-
- virtual void moneySpent(const std::string &txId, uint64_t amount) override
- {
- qDebug() << __FUNCTION__;
- emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
- }
-
-
- virtual void moneyReceived(const std::string &txId, uint64_t amount) override
- {
- qDebug() << __FUNCTION__;
- emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
- }
-
- virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override
- {
- qDebug() << __FUNCTION__;
- emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
- }
-
- virtual void newBlock(uint64_t height) override
- {
- // qDebug() << __FUNCTION__;
- emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
- }
-
- virtual void updated() override
- {
- emit m_wallet->updated();
- }
-
- // called when wallet refreshed by background thread or explicitly
- virtual void refreshed() override
- {
- qDebug() << __FUNCTION__;
- 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;
-};
-
Wallet::Wallet(QObject * parent)
: Wallet(nullptr, parent)
{
@@ -1021,6 +961,19 @@ void Wallet::keyReuseMitigation2(bool mitigation)
m_walletImpl->keyReuseMitigation2(mitigation);
}
+void Wallet::onWalletPassphraseNeeded(bool on_device)
+{
+ emit this->walletPassphraseNeeded(on_device);
+}
+
+void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
+{
+ if (m_walletListener != nullptr)
+ {
+ m_walletListener->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
+ }
+}
+
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
: QObject(parent)
, m_walletImpl(w)
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index 3450b1ce..f333f935 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -41,6 +41,8 @@
#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here;
#include "UnsignedTransaction.h"
#include "NetworkType.h"
+#include "PassphraseHelper.h"
+#include "WalletListenerImpl.h"
namespace Monero {
struct Wallet; // forward declaration
@@ -57,7 +59,7 @@ class SubaddressModel;
class SubaddressAccount;
class SubaddressAccountModel;
-class Wallet : public QObject
+class Wallet : public QObject, public PassprasePrompter
{
Q_OBJECT
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
@@ -348,6 +350,10 @@ public:
Q_INVOKABLE void segregationHeight(quint64 height);
Q_INVOKABLE void keyReuseMitigation2(bool mitigation);
+ // Passphrase entry for hardware wallets
+ Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
+ virtual void onWalletPassphraseNeeded(bool on_device) override;
+
// TODO: setListenter() when it implemented in API
signals:
// emitted on every event happened with wallet
@@ -367,6 +373,7 @@ signals:
void walletCreationHeightChanged();
void deviceButtonRequest(quint64 buttonCode);
void deviceButtonPressed();
+ void walletPassphraseNeeded(bool onDevice);
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
void deviceShowAddressShowed();
@@ -432,7 +439,7 @@ private:
bool m_connectionStatusRunning;
QString m_daemonUsername;
QString m_daemonPassword;
- Monero::WalletListener *m_walletListener;
+ WalletListenerImpl *m_walletListener;
FutureScheduler m_scheduler;
QMutex m_storeMutex;
};
diff --git a/src/libwalletqt/WalletListenerImpl.cpp b/src/libwalletqt/WalletListenerImpl.cpp
new file mode 100644
index 00000000..efcfa112
--- /dev/null
+++ b/src/libwalletqt/WalletListenerImpl.cpp
@@ -0,0 +1,97 @@
+// Copyright (c) 2014-2020, 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.
+
+#include "WalletListenerImpl.h"
+#include "Wallet.h"
+
+WalletListenerImpl::WalletListenerImpl(Wallet * w)
+ : m_wallet(w)
+ , m_phelper(w)
+{
+
+}
+
+void WalletListenerImpl::moneySpent(const std::string &txId, uint64_t amount)
+{
+ qDebug() << __FUNCTION__;
+ emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
+}
+
+void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount)
+{
+ qDebug() << __FUNCTION__;
+ emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
+}
+
+void WalletListenerImpl::unconfirmedMoneyReceived(const std::string &txId, uint64_t amount)
+{
+ qDebug() << __FUNCTION__;
+ emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
+}
+
+void WalletListenerImpl::newBlock(uint64_t height)
+{
+ // qDebug() << __FUNCTION__;
+ emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
+}
+
+void WalletListenerImpl::updated()
+{
+ emit m_wallet->updated();
+}
+
+// called when wallet refreshed by background thread or explicitly
+void WalletListenerImpl::refreshed()
+{
+ qDebug() << __FUNCTION__;
+ emit m_wallet->refreshed();
+}
+
+void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
+{
+ qDebug() << __FUNCTION__;
+ emit m_wallet->deviceButtonRequest(code);
+}
+
+void WalletListenerImpl::onDeviceButtonPressed()
+{
+ qDebug() << __FUNCTION__;
+ emit m_wallet->deviceButtonPressed();
+}
+
+void WalletListenerImpl::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
+{
+ qDebug() << __FUNCTION__;
+ m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
+}
+
+Monero::optional WalletListenerImpl::onDevicePassphraseRequest(bool & on_device)
+{
+ qDebug() << __FUNCTION__;
+ return m_phelper.onDevicePassphraseRequest(on_device);
+}
diff --git a/src/libwalletqt/WalletListenerImpl.h b/src/libwalletqt/WalletListenerImpl.h
new file mode 100644
index 00000000..9a134547
--- /dev/null
+++ b/src/libwalletqt/WalletListenerImpl.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2014-2020, 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.
+
+#ifndef MONERO_GUI_WALLETLISTENERIMPL_H
+#define MONERO_GUI_WALLETLISTENERIMPL_H
+
+#include "wallet/api/wallet2_api.h"
+#include "PassphraseHelper.h"
+
+class Wallet;
+
+class WalletListenerImpl : public Monero::WalletListener, public PassphraseReceiver
+{
+public:
+ WalletListenerImpl(Wallet * w);
+
+ virtual void moneySpent(const std::string &txId, uint64_t amount) override;
+
+ virtual void moneyReceived(const std::string &txId, uint64_t amount) override;
+
+ virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override;
+
+ virtual void newBlock(uint64_t height) override;
+
+ virtual void updated() override;
+
+ // called when wallet refreshed by background thread or explicitly
+ virtual void refreshed() override;
+
+ virtual void onDeviceButtonRequest(uint64_t code) override;
+
+ virtual void onDeviceButtonPressed() override;
+
+ virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override;
+
+ virtual Monero::optional onDevicePassphraseRequest(bool & on_device) override;
+
+private:
+ Wallet * m_wallet;
+ PassphraseHelper m_phelper;
+};
+
+#endif //MONERO_GUI_WALLETLISTENERIMPL_H
diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp
index a5c9e0ff..62836149 100644
--- a/src/libwalletqt/WalletManager.cpp
+++ b/src/libwalletqt/WalletManager.cpp
@@ -42,11 +42,12 @@
#include
#include "qt/updater.h"
+#include "qt/ScopeGuard.h"
-class WalletPassphraseListenerImpl : public Monero::WalletListener
+class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
{
public:
- WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_wallet(nullptr) {}
+ WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_phelper(mgr) {}
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; };
@@ -55,43 +56,33 @@ public:
virtual void updated() override {};
virtual void refreshed() override {};
- virtual Monero::optional onDevicePassphraseRequest(bool on_device) override
+ virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override
{
qDebug() << __FUNCTION__;
- if (on_device) return Monero::optional();
+ m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
+ }
- 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();
-
- return Monero::optional(tmpPass);
+ virtual Monero::optional onDevicePassphraseRequest(bool & on_device) override
+ {
+ qDebug() << __FUNCTION__;
+ return m_phelper.onDevicePassphraseRequest(on_device);
}
virtual void onDeviceButtonRequest(uint64_t code) override
{
- emit m_mgr->deviceButtonRequest(code);
+ qDebug() << __FUNCTION__;
+ 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;
+ emit m_mgr->deviceButtonPressed();
}
private:
WalletManager * m_mgr;
- Monero::Wallet * m_wallet;
+ PassphraseHelper m_phelper;
};
WalletManager * WalletManager::m_instance = nullptr;
@@ -123,6 +114,13 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
{
QMutexLocker locker(&m_mutex);
WalletPassphraseListenerImpl tmpListener(this);
+ m_mutex_passphraseReceiver.lock();
+ m_passphraseReceiver = &tmpListener;
+ m_mutex_passphraseReceiver.unlock();
+ const auto cleanup = sg::make_scope_guard([this]() noexcept {
+ QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
+ this->m_passphraseReceiver = nullptr;
+ });
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
@@ -186,6 +184,13 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
{
QMutexLocker locker(&m_mutex);
WalletPassphraseListenerImpl tmpListener(this);
+ m_mutex_passphraseReceiver.lock();
+ m_passphraseReceiver = &tmpListener;
+ m_mutex_passphraseReceiver.unlock();
+ const auto cleanup = sg::make_scope_guard([this]() noexcept {
+ QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
+ this->m_passphraseReceiver = nullptr;
+ });
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
@@ -529,6 +534,7 @@ bool WalletManager::clearWalletCache(const QString &wallet_path) const
WalletManager::WalletManager(QObject *parent)
: QObject(parent)
+ , m_passphraseReceiver(nullptr)
, m_scheduler(this)
{
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
@@ -539,22 +545,16 @@ WalletManager::~WalletManager()
m_scheduler.shutdownWaitForFinished();
}
-void WalletManager::onWalletPassphraseNeeded(Monero::Wallet *)
+void WalletManager::onWalletPassphraseNeeded(bool on_device)
{
- m_mutex_pass.lock();
- m_passphrase_abort = false;
- emit this->walletPassphraseNeeded();
-
- m_cond_pass.wait(&m_mutex_pass);
- m_mutex_pass.unlock();
+ emit this->walletPassphraseNeeded(on_device);
}
-void WalletManager::onPassphraseEntered(const QString &passphrase, bool entry_abort)
+void WalletManager::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
{
- m_mutex_pass.lock();
- m_passphrase = passphrase;
- m_passphrase_abort = entry_abort;
-
- m_cond_pass.wakeAll();
- m_mutex_pass.unlock();
+ QMutexLocker locker(&m_mutex_passphraseReceiver);
+ if (m_passphraseReceiver != nullptr)
+ {
+ m_passphraseReceiver->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
+ }
}
diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h
index 01767716..3564ad61 100644
--- a/src/libwalletqt/WalletManager.h
+++ b/src/libwalletqt/WalletManager.h
@@ -38,13 +38,14 @@
#include
#include "qt/FutureScheduler.h"
#include "NetworkType.h"
+#include "PassphraseHelper.h"
class Wallet;
namespace Monero {
struct WalletManager;
}
-class WalletManager : public QObject
+class WalletManager : public QObject, public PassprasePrompter
{
Q_OBJECT
Q_PROPERTY(bool connected READ connected)
@@ -185,14 +186,14 @@ 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);
+ Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
+ virtual void onWalletPassphraseNeeded(bool on_device) override;
signals:
void walletOpened(Wallet * wallet);
void walletCreated(Wallet * wallet);
- void walletPassphraseNeeded();
+ void walletPassphraseNeeded(bool onDevice);
void deviceButtonRequest(quint64 buttonCode);
void deviceButtonPressed();
void checkUpdatesComplete(
@@ -216,12 +217,8 @@ private:
Monero::WalletManager * m_pimpl;
mutable QMutex m_mutex;
QPointer m_currentWallet;
-
- QWaitCondition m_cond_pass;
- QMutex m_mutex_pass;
- QString m_passphrase;
- bool m_passphrase_abort;
-
+ PassphraseReceiver * m_passphraseReceiver;
+ QMutex m_mutex_passphraseReceiver;
FutureScheduler m_scheduler;
};
diff --git a/src/qt/ScopeGuard.h b/src/qt/ScopeGuard.h
new file mode 100644
index 00000000..6d5df044
--- /dev/null
+++ b/src/qt/ScopeGuard.h
@@ -0,0 +1,205 @@
+// Author: ricab
+// Source: https://github.com/ricab/scope_guard
+//
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to
+
+#ifndef SCOPE_GUARD_HPP_
+#define SCOPE_GUARD_HPP_
+
+#include
+#include
+
+#if __cplusplus >= 201703L && defined(SG_REQUIRE_NOEXCEPT_IN_CPP17)
+#define SG_REQUIRE_NOEXCEPT
+#endif
+
+namespace sg
+{
+ namespace detail
+ {
+ /* --- Some custom type traits --- */
+
+ // Type trait determining whether a type is callable with no arguments
+ template
+ struct is_noarg_callable_t
+ : public std::false_type
+ {}; // in general, false
+
+ template
+ struct is_noarg_callable_t()())>
+ : public std::true_type
+ {}; // only true when call expression valid
+
+ // Type trait determining whether a no-argument callable returns void
+ template
+ struct returns_void_t
+ : public std::is_same()())>
+ {};
+
+ /* Type trait determining whether a no-arg callable is nothrow invocable if
+ required. This is where SG_REQUIRE_NOEXCEPT logic is encapsulated. */
+ template
+ struct is_nothrow_invocable_if_required_t
+ : public
+#ifdef SG_REQUIRE_NOEXCEPT
+ std::is_nothrow_invocable /* Note: _r variants not enough to
+ confirm void return: any return can be
+ discarded so all returns are
+ compatible with void */
+#else
+ std::true_type
+#endif
+ {};
+
+ // logic AND of two or more type traits
+ template
+ struct and_t : public and_t>
+ {}; // for more than two arguments
+
+ template
+ struct and_t : public std::conditional::type
+ {}; // for two arguments
+
+ // Type trait determining whether a type is a proper scope_guard callback.
+ template
+ struct is_proper_sg_callback_t
+ : public and_t,
+ returns_void_t,
+ is_nothrow_invocable_if_required_t,
+ std::is_nothrow_destructible>
+ {};
+
+
+ /* --- The actual scope_guard template --- */
+
+ template::value>::type>
+ class scope_guard;
+
+
+ /* --- Now the friend maker --- */
+
+ template
+ detail::scope_guard make_scope_guard(Callback&& callback)
+ noexcept(std::is_nothrow_constructible::value); /*
+ we need this in the inner namespace due to MSVC bugs preventing
+ sg::detail::scope_guard from befriending a sg::make_scope_guard
+ template instance in the parent namespace (see https://is.gd/xFfFhE). */
+
+
+ /* --- The template specialization that actually defines the class --- */
+
+ template
+ class scope_guard final
+ {
+ public:
+ typedef Callback callback_type;
+
+ scope_guard(scope_guard&& other)
+ noexcept(std::is_nothrow_constructible::value);
+
+ ~scope_guard() noexcept; // highlight noexcept dtor
+
+ void dismiss() noexcept;
+
+ public:
+ scope_guard() = delete;
+ scope_guard(const scope_guard&) = delete;
+ scope_guard& operator=(const scope_guard&) = delete;
+ scope_guard& operator=(scope_guard&&) = delete;
+
+ private:
+ explicit scope_guard(Callback&& callback)
+ noexcept(std::is_nothrow_constructible::value); /*
+ meant for friends only */
+
+ friend scope_guard make_scope_guard(Callback&&)
+ noexcept(std::is_nothrow_constructible::value); /*
+ only make_scope_guard can create scope_guards from scratch (i.e. non-move)
+ */
+
+ private:
+ Callback m_callback;
+ bool m_active;
+
+ };
+
+} // namespace detail
+
+
+/* --- Now the single public maker function --- */
+
+using detail::make_scope_guard; // see comment on declaration above
+
+} // namespace sg
+
+////////////////////////////////////////////////////////////////////////////////
+template
+sg::detail::scope_guard::scope_guard(Callback&& callback)
+noexcept(std::is_nothrow_constructible::value)
+ : m_callback(std::forward(callback)) /* use () instead of {} because
+ of DR 1467 (https://is.gd/WHmWuo), which still impacts older compilers
+ (e.g. GCC 4.x and clang <=3.6, see https://godbolt.org/g/TE9tPJ and
+ https://is.gd/Tsmh8G) */
+ , m_active{true}
+{}
+
+////////////////////////////////////////////////////////////////////////////////
+template
+sg::detail::scope_guard::~scope_guard() noexcept
+{
+ if(m_active)
+ m_callback();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+template
+sg::detail::scope_guard::scope_guard(scope_guard&& other)
+noexcept(std::is_nothrow_constructible::value)
+ : m_callback(std::forward(other.m_callback)) // idem
+ , m_active{std::move(other.m_active)}
+{
+ other.m_active = false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+template
+inline void sg::detail::scope_guard::dismiss() noexcept
+{
+ m_active = false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+template
+inline auto sg::detail::make_scope_guard(Callback&& callback)
+noexcept(std::is_nothrow_constructible::value)
+-> detail::scope_guard
+{
+ return detail::scope_guard{std::forward(callback)};
+}
+
+#endif /* SCOPE_GUARD_HPP_ */
diff --git a/wizard/WizardController.qml b/wizard/WizardController.qml
index a1628b4f..b38d079f 100644
--- a/wizard/WizardController.qml
+++ b/wizard/WizardController.qml
@@ -490,19 +490,24 @@ Rectangle {
walletCreatedFromDevice(success);
}
- function onWalletPassphraseNeeded(){
+ function onWalletPassphraseNeeded(on_device){
splash.close()
console.log(">>> wallet passphrase needed: ");
- passwordDialog.onAcceptedPassphraseCallback = function() {
- walletManager.onPassphraseEntered(passwordDialog.password);
+ devicePassphraseDialog.onAcceptedCallback = function(passphrase) {
+ walletManager.onPassphraseEntered(passphrase, false, false);
creatingWalletDeviceSplash();
}
- passwordDialog.onRejectedPassphraseCallback = function() {
- walletManager.onPassphraseEntered("", true);
+ devicePassphraseDialog.onWalletEntryCallback = function() {
+ walletManager.onPassphraseEntered("", true, false);
creatingWalletDeviceSplash();
}
- passwordDialog.openPassphraseDialog()
+ devicePassphraseDialog.onRejectedCallback = function() {
+ walletManager.onPassphraseEntered("", false, true);
+ creatingWalletDeviceSplash();
+ }
+
+ devicePassphraseDialog.open(on_device)
}
function onDeviceButtonRequest(code){