mirror of
https://github.com/monero-project/monero-gui.git
synced 2024-12-23 03:59:38 +00:00
Merge pull request #2037
1a2675b
async device open and create from device, passphrase (ph4r05)
This commit is contained in:
commit
cd65cc12da
10 changed files with 607 additions and 14 deletions
327
components/PassphraseDialog.qml
Normal file
327
components/PassphraseDialog.qml
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
65
main.qml
65
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
|
||||
|
|
1
qml.qrc
1
qml.qrc
|
@ -117,6 +117,7 @@
|
|||
<file>pages/TxKey.qml</file>
|
||||
<file>pages/SharedRingDB.qml</file>
|
||||
<file>components/IconButton.qml</file>
|
||||
<file>components/PassphraseDialog.qml</file>
|
||||
<file>components/PasswordDialog.qml</file>
|
||||
<file>components/NewPasswordDialog.qml</file>
|
||||
<file>components/InputDialog.qml</file>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -13,6 +13,57 @@
|
|||
#include <QMutexLocker>
|
||||
#include <QString>
|
||||
|
||||
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<std::string> onDevicePassphraseRequest(bool on_device) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
if (on_device) return Monero::optional<std::string>();
|
||||
|
||||
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<std::string>(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<Monero::NetworkType>(nettype), kdfRounds);
|
||||
Monero::Wallet * w = m_pimpl->openWallet(path.toStdString(), password.toStdString(), static_cast<Monero::NetworkType>(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<Monero::NetworkType>(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<Wallet *> future = QtConcurrent::run(lmbd);
|
||||
|
||||
QFutureWatcher<Wallet *> * watcher = new QFutureWatcher<Wallet *>();
|
||||
|
||||
connect(watcher, &QFutureWatcher<Wallet *>::finished,
|
||||
this, [this, watcher]() {
|
||||
QFuture<Wallet *> 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();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <wallet/api/wallet2_api.h>
|
||||
#include <QMutex>
|
||||
#include <QPointer>
|
||||
#include <QWaitCondition>
|
||||
#include <QMutex>
|
||||
#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<Wallet> m_currentWallet;
|
||||
|
||||
QWaitCondition m_cond_pass;
|
||||
QMutex m_mutex_pass;
|
||||
QString m_passphrase;
|
||||
bool m_passphrase_abort;
|
||||
};
|
||||
|
||||
#endif // WALLETMANAGER_H
|
||||
|
|
|
@ -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(){
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue