Skip to content

Commit

Permalink
Merge pull request #1496
Browse files Browse the repository at this point in the history
057b5cd Add support for creating hardware wallet (stoffu)
5029bae get_libwallet_api: checkout v0.12.3.0 (stoffu)
  • Loading branch information
fluffypony committed Jul 12, 2018
2 parents f5792cc + 057b5cd commit 496e69b
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 9 deletions.
2 changes: 1 addition & 1 deletion get_libwallet_api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if [ ! -d $MONERO_DIR/src ]; then
fi
git submodule update --remote
git -C $MONERO_DIR fetch
git -C $MONERO_DIR checkout v0.12.2.0
git -C $MONERO_DIR checkout v0.12.3.0

# get monero core tag
get_tag
Expand Down
Binary file added images/createWalletFromDevice.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ 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);
}

function walletPath() {
Expand Down Expand Up @@ -1014,6 +1014,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
Expand Down
2 changes: 2 additions & 0 deletions qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@
<file>images/prevPage.png</file>
<file>wizard/WizardOptions.qml</file>
<file>images/createWallet.png</file>
<file>images/createWalletFromDevice.png</file>
<file>images/openAccount.png</file>
<file>images/recoverWallet.png</file>
<file>wizard/WizardCreateWallet.qml</file>
<file>wizard/WizardCreateWalletFromDevice.qml</file>
<file>images/greyTriangle.png</file>
<file>images/copyToClipboard.png</file>
<file>wizard/WizardPassword.qml</file>
Expand Down
12 changes: 9 additions & 3 deletions src/libwalletqt/Wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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
Expand All @@ -201,7 +207,7 @@ void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLim
}

QFuture<bool> future = QtConcurrent::run(this, &Wallet::init,
daemonAddress, upperTransactionLimit, isRecovering, restoreHeight);
daemonAddress, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight);
QFutureWatcher<bool> * watcher = new QFutureWatcher<bool>();

connect(watcher, &QFutureWatcher<bool>::finished,
Expand Down
4 changes: 2 additions & 2 deletions src/libwalletqt/Wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ class Wallet : public QObject
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 = "");
Expand Down
14 changes: 14 additions & 0 deletions src/libwalletqt/WalletManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, uint64_t 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<Monero::NetworkType>(nettype),
deviceName.toStdString(), restoreHeight, subaddressLookahead.toStdString());
m_currentWallet = new Wallet(w);
return m_currentWallet;
}

QString WalletManager::closeWallet()
{
Expand Down
6 changes: 6 additions & 0 deletions src/libwalletqt/WalletManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ class WalletManager : public QObject
const QString &spendkey = "",
quint64 restoreHeight = 0);

Q_INVOKABLE Wallet * createWalletFromDevice(const QString &path,
const QString &password,
NetworkType::Type nettype,
const QString &deviceName,
uint64_t restoreHeight = 0,
const QString &subaddressLookahead = "");
/*!
* \brief closeWallet - closes current open wallet and frees memory
* \return wallet address
Expand Down
123 changes: 123 additions & 0 deletions wizard/WizardCreateWalletFromDevice.qml
Original file line number Diff line number Diff line change
@@ -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)
}
}
19 changes: 19 additions & 0 deletions wizard/WizardMain.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -263,6 +275,7 @@ ColumnLayout {
onCreateWalletClicked: wizard.openCreateWalletPage()
onRecoveryWalletClicked: wizard.openRecoveryWalletPage()
onOpenWalletClicked: wizard.openOpenWalletPage();
onCreateWalletFromDeviceClicked: wizard.openCreateWalletFromDevicePage()
}

WizardCreateWallet {
Expand All @@ -283,6 +296,12 @@ ColumnLayout {
Layout.topMargin: wizardTopMargin
}

WizardCreateWalletFromDevice {
id: createWalletFromDevicePage
Layout.bottomMargin: wizardBottomMargin
Layout.topMargin: wizardTopMargin
}

WizardPassword {
id: passwordPage
Layout.bottomMargin: wizardBottomMargin
Expand Down
54 changes: 53 additions & 1 deletion wizard/WizardManageWalletUI.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(){
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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): <major>:<minor>") + 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
Expand Down
Loading

0 comments on commit 496e69b

Please sign in to comment.