From 9bb6a25f311bccff3d089ae0b0a4156b16d6e109 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 4 Jun 2018 17:05:29 +0900 Subject: [PATCH] Add support for creating hardware wallet --- images/createWalletFromDevice.png | Bin 0 -> 10665 bytes main.qml | 8 +- qml.qrc | 2 + src/libwalletqt/Wallet.cpp | 12 ++- src/libwalletqt/Wallet.h | 4 +- src/libwalletqt/WalletManager.cpp | 14 +++ src/libwalletqt/WalletManager.h | 6 ++ wizard/WizardCreateWalletFromDevice.qml | 123 ++++++++++++++++++++++++ wizard/WizardMain.qml | 19 ++++ wizard/WizardManageWalletUI.qml | 54 ++++++++++- wizard/WizardOptions.qml | 48 ++++++++- 11 files changed, 282 insertions(+), 8 deletions(-) create mode 100755 images/createWalletFromDevice.png create mode 100644 wizard/WizardCreateWalletFromDevice.qml diff --git a/images/createWalletFromDevice.png b/images/createWalletFromDevice.png new file mode 100755 index 0000000000000000000000000000000000000000..9aa1e2f458a05d32a512216381e4287416fe5488 GIT binary patch literal 10665 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 P00000NkvXXu0mjfOW~|H literal 0 HcmV?d00001 diff --git a/main.qml b/main.qml index 07035d53..3f5769ab 100644 --- a/main.qml +++ b/main.qml @@ -222,6 +222,9 @@ ApplicationWindow { if (typeof wizard.m_wallet !== 'undefined') { console.log("using wizard wallet") //Set restoreHeight + if (persistentSettings.restore_height == 0 && persistentSettings.is_recovering_from_device && walletManager.localDaemonSynced()) { + persistentSettings.restore_height = walletManager.blockchainHeight() - 1; + } if(persistentSettings.restore_height > 0){ // We store restore height in own variable for performance reasons. restoreHeight = persistentSettings.restore_height @@ -331,7 +334,9 @@ ApplicationWindow { currentDaemonAddress = localDaemonAddress console.log("initializing with daemon address: ", currentDaemonAddress) - currentWallet.initAsync(currentDaemonAddress, 0, persistentSettings.is_recovering, persistentSettings.restore_height); + currentWallet.initAsync(currentDaemonAddress, 0, persistentSettings.is_recovering, persistentSettings.is_recovering_from_device, persistentSettings.restore_height); + // save wallet keys in case wallet settings have been changed in the init + currentWallet.setPassword(walletPassword); } function walletPath() { @@ -1014,6 +1019,7 @@ ApplicationWindow { property string payment_id property int restore_height : 0 property bool is_recovering : false + property bool is_recovering_from_device : false property bool customDecorations : true property string daemonFlags property int logLevel: 0 diff --git a/qml.qrc b/qml.qrc index cf30e6c7..e0e82674 100644 --- a/qml.qrc +++ b/qml.qrc @@ -85,9 +85,11 @@ images/prevPage.png wizard/WizardOptions.qml images/createWallet.png + images/createWalletFromDevice.png images/openAccount.png images/recoverWallet.png wizard/WizardCreateWallet.qml + wizard/WizardCreateWalletFromDevice.qml images/greyTriangle.png images/copyToClipboard.png wizard/WizardPassword.qml diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 0047f8dd..2c6e92c9 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -172,12 +172,18 @@ bool Wallet::store(const QString &path) return m_walletImpl->store(path.toStdString()); } -bool Wallet::init(const QString &daemonAddress, quint64 upperTransactionLimit, bool isRecovering, quint64 restoreHeight) +bool Wallet::init(const QString &daemonAddress, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight) { qDebug() << "init non async"; if (isRecovering){ qDebug() << "RESTORING"; m_walletImpl->setRecoveringFromSeed(true); + } + if (isRecoveringFromDevice){ + qDebug() << "RESTORING FROM DEVICE"; + m_walletImpl->setRecoveringFromDevice(true); + } + if (isRecovering || isRecoveringFromDevice) { m_walletImpl->setRefreshFromBlockHeight(restoreHeight); } m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString()); @@ -191,7 +197,7 @@ void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemon m_daemonPassword = daemonPassword; } -void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLimit, bool isRecovering, quint64 restoreHeight) +void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight) { qDebug() << "initAsync: " + daemonAddress; // Change status to disconnected if connected @@ -201,7 +207,7 @@ void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLim } QFuture future = QtConcurrent::run(this, &Wallet::init, - daemonAddress, upperTransactionLimit, isRecovering, restoreHeight); + daemonAddress, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight); QFutureWatcher * watcher = new QFutureWatcher(); connect(watcher, &QFutureWatcher::finished, diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index df413363..d0f053ac 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -111,10 +111,10 @@ public: Q_INVOKABLE bool store(const QString &path = ""); //! initializes wallet - Q_INVOKABLE bool init(const QString &daemonAddress, quint64 upperTransactionLimit = 0, bool isRecovering = false, quint64 restoreHeight = 0); + Q_INVOKABLE bool init(const QString &daemonAddress, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0); //! initializes wallet asynchronously - Q_INVOKABLE void initAsync(const QString &daemonAddress, quint64 upperTransactionLimit = 0, bool isRecovering = false, quint64 restoreHeight = 0); + Q_INVOKABLE void initAsync(const QString &daemonAddress, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0); // Set daemon rpc user/pass Q_INVOKABLE void setDaemonLogin(const QString &daemonUsername = "", const QString &daemonPassword = ""); diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index f6eb771a..355527ad 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -104,6 +104,20 @@ Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString & return m_currentWallet; } +Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString &password, NetworkType::Type nettype, + const QString &deviceName, quint64 restoreHeight, const QString &subaddressLookahead) +{ + QMutexLocker locker(&m_mutex); + 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()); + m_currentWallet = new Wallet(w); + return m_currentWallet; +} QString WalletManager::closeWallet() { diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index dd44c634..75626928 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -62,6 +62,12 @@ public: const QString &spendkey = "", quint64 restoreHeight = 0); + Q_INVOKABLE Wallet * createWalletFromDevice(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 diff --git a/wizard/WizardCreateWalletFromDevice.qml b/wizard/WizardCreateWalletFromDevice.qml new file mode 100644 index 00000000..ae63e2a0 --- /dev/null +++ b/wizard/WizardCreateWalletFromDevice.qml @@ -0,0 +1,123 @@ +// Copyright (c) 2014-2018, 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.2 +import moneroComponents.WalletManager 1.0 +import moneroComponents.Wallet 1.0 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 +import 'utils.js' as Utils + +ColumnLayout { + opacity: 0 + visible: false + + Behavior on opacity { + NumberAnimation { duration: 100; easing.type: Easing.InQuad } + } + + + onOpacityChanged: visible = opacity !== 0 + + function onWizardRestarted() { + // reset account name field + uiItem.accountNameText = defaultAccountName + } + + //! function called each time we display this page + + function onPageOpened(settingsOblect) { + uiItem.checkNextButton() + uiItem.deviceNameDropdown.update() + } + + function onPageClosed(settingsObject) { + settingsObject['account_name'] = uiItem.accountNameText + settingsObject['wallet_path'] = uiItem.walletPath + var restoreHeight = parseInt(uiItem.restoreHeight); + settingsObject['restore_height'] = isNaN(restoreHeight)? 0 : restoreHeight; + settingsObject['subaddress_lookahead'] = uiItem.subaddressLookahead; + settingsObject['deviceName'] = uiItem.deviceName; + var walletFullPath = wizard.createWalletPath(uiItem.walletPath,uiItem.accountNameText); + if (!wizard.walletPathValid(walletFullPath)) { + return false; + } + return createWalletFromDevice(settingsObject) + } + + //! function called each time we hide this page + // + + + function createWalletFromDevice(settingsObject) { + // 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 + + // Always delete the wallet object before creating new - we could be stepping back from recovering wallet + if (typeof m_wallet !== 'undefined') { + walletManager.closeWallet() + console.log("deleting wallet") + } + + var tmp_wallet_filename = oshelper.temporaryFilename(); + console.log("Creating temporary wallet", tmp_wallet_filename) + var nettype = appWindow.persistentSettings.nettype; + var restoreHeight = settingsObject.restore_height; + var subaddressLookahead = settingsObject.subaddress_lookahead; + var deviceName = settingsObject.deviceName; + + var wallet = walletManager.createWalletFromDevice(tmp_wallet_filename, "", nettype, deviceName, restoreHeight, subaddressLookahead); + + var success = wallet.status === Wallet.Status_Ok; + if (success) { + m_wallet = wallet; + settingsObject['is_recovering_from_device'] = true; + settingsObject['tmp_wallet_filename'] = tmp_wallet_filename + } else { + console.log(wallet.errorString) + walletErrorDialog.text = wallet.errorString; + walletErrorDialog.open(); + walletManager.closeWallet(); + } + return success; + } + + WizardManageWalletUI { + id: uiItem + titleText: qsTr("Create a new wallet from hardware device") + translationManager.emptyString + wordsTextItem.clipboardButtonVisible: false + wordsTextItem.tipTextVisible: false + restoreHeightVisible:true + recoverMode: false + recoverFromDevice: true + } + + Component.onCompleted: { + parent.wizardRestarted.connect(onWizardRestarted) + } +} diff --git a/wizard/WizardMain.qml b/wizard/WizardMain.qml index ec685fe4..ae94ed38 100644 --- a/wizard/WizardMain.qml +++ b/wizard/WizardMain.qml @@ -54,6 +54,7 @@ ColumnLayout { "create_wallet" : [welcomePage, optionsPage, createWalletPage, passwordPage, daemonSettingsPage, finishPage ], "recovery_wallet" : [welcomePage, optionsPage, recoveryWalletPage, passwordPage, daemonSettingsPage, finishPage ], "create_view_only_wallet" : [ createViewOnlyWalletPage, passwordPage ], + "create_wallet_from_device" : [welcomePage, optionsPage, createWalletFromDevicePage, passwordPage, daemonSettingsPage, finishPage ], } property string currentPath: "create_wallet" @@ -162,6 +163,16 @@ ColumnLayout { rootItem.state = "wizard"; } + function openCreateWalletFromDevicePage() { + wizardRestarted(); + print ("show create wallet from device page"); + currentPath = "create_wallet_from_device" + pages = paths[currentPath] + wizard.nextButton.visible = true + // goto next page + switchPage(true); + } + function createWalletPath(folder_path,account_name){ // Remove trailing slash - (default on windows and mac) @@ -233,6 +244,7 @@ ColumnLayout { appWindow.persistentSettings.auto_donations_amount = false //settings.auto_donations_amount appWindow.persistentSettings.restore_height = (isNaN(settings.restore_height))? 0 : settings.restore_height appWindow.persistentSettings.is_recovering = (settings.is_recovering === undefined)? false : settings.is_recovering + appWindow.persistentSettings.is_recovering_from_device = (settings.is_recovering_from_device === undefined)? false : settings.is_recovering_from_device } // reading settings from persistent storage @@ -263,6 +275,7 @@ ColumnLayout { onCreateWalletClicked: wizard.openCreateWalletPage() onRecoveryWalletClicked: wizard.openRecoveryWalletPage() onOpenWalletClicked: wizard.openOpenWalletPage(); + onCreateWalletFromDeviceClicked: wizard.openCreateWalletFromDevicePage() } WizardCreateWallet { @@ -283,6 +296,12 @@ ColumnLayout { Layout.topMargin: wizardTopMargin } + WizardCreateWalletFromDevice { + id: createWalletFromDevicePage + Layout.bottomMargin: wizardBottomMargin + Layout.topMargin: wizardTopMargin + } + WizardPassword { id: passwordPage Layout.bottomMargin: wizardBottomMargin diff --git a/wizard/WizardManageWalletUI.qml b/wizard/WizardManageWalletUI.qml index a0c268cf..f1b853e1 100644 --- a/wizard/WizardManageWalletUI.qml +++ b/wizard/WizardManageWalletUI.qml @@ -44,6 +44,7 @@ ColumnLayout { property alias wordsTextItem : memoTextItem property alias restoreHeight : restoreHeightItem.text property alias restoreHeightVisible: restoreHeightItem.visible + property alias subaddressLookahead : subaddressLookaheadItem.text property alias walletName : accountName.text property alias progressDotsModel : progressDots.model property alias recoverFromKeysAddress: addressLine.text; @@ -53,6 +54,10 @@ ColumnLayout { property bool recoverMode: false // Recover form seed or keys property bool recoverFromSeedMode: true + // Recover form hardware device + property bool recoverFromDevice: false + property var deviceName: deviceNameModel.get(deviceNameDropdown.currentIndex).column2 + property alias deviceNameDropdown: deviceNameDropdown property int rowSpacing: 10 function checkFields(){ @@ -215,7 +220,7 @@ ColumnLayout { // Recover from seed RowLayout { id: recoverFromSeed - visible: !recoverMode || ( recoverMode && recoverFromSeedMode) + visible: !recoverFromDevice && (!recoverMode || ( recoverMode && recoverFromSeedMode)) WizardMemoTextInput { id : memoTextItem Layout.fillWidth: true @@ -303,9 +308,56 @@ ColumnLayout { fontBold: false } } + + // Subaddress lookahead + RowLayout { + visible: recoverFromDevice + LineEdit { + id: subaddressLookaheadItem + Layout.fillWidth: true + Layout.maximumWidth: 600 * scaleRatio + Layout.minimumWidth: 200 * scaleRatio + placeholderFontBold: true + placeholderFontFamily: "Arial" + placeholderColor: Style.legacy_placeholderFontColor + placeholderText: qsTr("Subaddress lookahead (optional): :") + translationManager.emptyString + placeholderOpacity: 1.0 + borderColor: Qt.rgba(0, 0, 0, 0.15) + backgroundColor: "white" + fontColor: "black" + fontBold: false + } + } + + // Device name + ColumnLayout { + visible: recoverFromDevice + Label { + Layout.topMargin: 20 * scaleRatio + fontFamily: "Arial" + fontColor: "#555555" + fontSize: 14 * scaleRatio + text: qsTr("Device name") + translationManager.emptyString + } + ListModel { + id: deviceNameModel + ListElement { column1: qsTr("Ledger") ; column2: "Ledger"; } +// ListElement { column1: qsTr("Trezor") ; column2: "Trezor"; } + } + StandardDropdown { + id: deviceNameDropdown + dataModel: deviceNameModel + Layout.fillWidth: true + Layout.topMargin: 6 + colorHeaderBackground: "black" + releasedColor: "#363636" + pressedColor: "#202020" + } + } // Wallet store location ColumnLayout { + z: deviceNameDropdown.z - 1 Label { Layout.fillWidth: true Layout.topMargin: 20 * scaleRatio diff --git a/wizard/WizardOptions.qml b/wizard/WizardOptions.qml index 65a32582..3406c036 100644 --- a/wizard/WizardOptions.qml +++ b/wizard/WizardOptions.qml @@ -37,9 +37,10 @@ ColumnLayout { signal createWalletClicked() signal recoveryWalletClicked() signal openWalletClicked() + signal createWalletFromDeviceClicked() opacity: 0 visible: false - property int buttonSize: (isMobile) ? 80 * scaleRatio : 190 * scaleRatio + property int buttonSize: (isMobile) ? 80 * scaleRatio : 140 * scaleRatio property int buttonImageSize: (isMobile) ? buttonSize - 10 * scaleRatio : buttonSize - 30 * scaleRatio function onPageClosed() { @@ -227,6 +228,51 @@ ColumnLayout { wrapMode: Text.WordWrap } } + + GridLayout { + Layout.fillHeight: true + Layout.fillWidth: true + flow: !isMobile ? GridLayout.TopToBottom : GridLayout.LeftToRight + rowSpacing: 20 * scaleRatio + columnSpacing: 10 * scaleRatio + + Rectangle { + Layout.preferredHeight: page.buttonSize + Layout.preferredWidth: page.buttonSize + radius: page.buttonSize + color: createWalletFromDeviceArea.containsMouse ? "#DBDBDB" : "#FFFFFF" + + + Image { + width: page.buttonImageSize + height: page.buttonImageSize + fillMode: Image.PreserveAspectFit + horizontalAlignment: Image.AlignRight + verticalAlignment: Image.AlignTop + anchors.centerIn: parent + source: "qrc:///images/createWalletFromDevice.png" + } + + MouseArea { + id: createWalletFromDeviceArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + page.createWalletFromDeviceClicked() + } + } + } + + Text { + Layout.preferredWidth: page.buttonSize + font.family: "Arial" + font.pixelSize: 16 * scaleRatio + color: "#4A4949" + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + text: qsTr("Create a new wallet from hardware device") + translationManager.emptyString + } + } } ColumnLayout {