Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: sync in the background with just view key when locked #4050

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion components/PasswordDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Item {
property bool passwordDialogMode
property bool passphraseDialogMode
property bool newPasswordDialogMode
property bool backgroundSyncing

// same signals as Dialog has
signal accepted()
Expand Down Expand Up @@ -77,10 +78,11 @@ Item {
appWindow.updateBalance();
}

function open(walletName, errorText, okButtonText, okButtonIcon) {
function open(walletName, errorText, okButtonText, okButtonIcon, backgroundSyncOn) {
passwordDialogMode = true;
passphraseDialogMode = false;
newPasswordDialogMode = false;
backgroundSyncing = backgroundSyncOn || false;
root.okButtonText = okButtonText;
root.okButtonIcon = okButtonIcon ? okButtonIcon : "";
_openInit(walletName, errorText);
Expand All @@ -90,13 +92,15 @@ Item {
passwordDialogMode = false;
passphraseDialogMode = true;
newPasswordDialogMode = false;
backgroundSyncing = false;
_openInit("", "");
}

function openNewPasswordDialog() {
passwordDialogMode = false;
passphraseDialogMode = false;
newPasswordDialogMode = true;
backgroundSyncing = false;
_openInit("", "");
}

Expand Down Expand Up @@ -300,6 +304,18 @@ Item {
onClicked: onOk()
}
}

Label {
visible: backgroundSyncing
text: qsTr("Syncing in the background...") + translationManager.emptyString;
Layout.fillWidth: true

font.pixelSize: 14
font.family: MoneroComponents.Style.fontLight.name
font.italic: true

color: MoneroComponents.Style.defaultFontColor
}
}
}
}
108 changes: 97 additions & 11 deletions main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ ApplicationWindow {
property string prevSplashText;
property bool splashDisplayedBeforeButtonRequest;
property bool themeTransition: false
property int backgroundSyncType: Wallet.BackgroundSync_Off;

// fiat price conversion
property real fiatPrice: 0
Expand Down Expand Up @@ -133,6 +134,12 @@ ApplicationWindow {
}

function lock() {
if (currentWallet && currentWallet.getBackgroundSyncType() != Wallet.BackgroundSync_Off) {
appWindow.showProcessingSplash(qsTr("Locking..."));
currentWallet.startBackgroundSync()
return;
}

passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
passwordDialog.onAcceptedCallback = function() {
if(walletPassword === passwordDialog.password)
Expand Down Expand Up @@ -288,6 +295,9 @@ ApplicationWindow {
currentWallet.heightRefreshed.disconnect(onHeightRefreshed);
currentWallet.refreshed.disconnect(onWalletRefresh)
currentWallet.updated.disconnect(onWalletUpdate)
currentWallet.backgroundSyncSetup.disconnect(onBackgroundSyncSetup)
currentWallet.backgroundSyncStarted.disconnect(onBackgroundSyncStarted)
currentWallet.backgroundSyncStopped.disconnect(onBackgroundSyncStopped)
currentWallet.newBlock.disconnect(onWalletNewBlock)
currentWallet.moneySpent.disconnect(onWalletMoneySent)
currentWallet.moneyReceived.disconnect(onWalletMoneyReceived)
Expand Down Expand Up @@ -324,6 +334,7 @@ ApplicationWindow {
walletName = usefulName(wallet.path)

viewOnly = currentWallet.viewOnly;
backgroundSyncType = currentWallet.getBackgroundSyncType();

// New wallets saves the testnet flag in keys file.
if(persistentSettings.nettype != currentWallet.nettype) {
Expand All @@ -335,6 +346,9 @@ ApplicationWindow {
currentWallet.heightRefreshed.connect(onHeightRefreshed);
currentWallet.refreshed.connect(onWalletRefresh)
currentWallet.updated.connect(onWalletUpdate)
currentWallet.backgroundSyncSetup.connect(onBackgroundSyncSetup)
currentWallet.backgroundSyncStarted.connect(onBackgroundSyncStarted)
currentWallet.backgroundSyncStopped.connect(onBackgroundSyncStopped)
currentWallet.newBlock.connect(onWalletNewBlock)
currentWallet.moneySpent.connect(onWalletMoneySent)
currentWallet.moneyReceived.connect(onWalletMoneyReceived)
Expand Down Expand Up @@ -544,6 +558,15 @@ ApplicationWindow {
}
}

// Don't allow opening background wallets in the GUI
if (wallet.isBackgroundWallet()) {
passwordDialog.onCancel();
appWindow.showStatusMessage(qsTr("Can't open background wallets in the GUI"),6);
console.log("closing background wallet");
closeWallet();
return;
}

// wallet opened successfully, subscribing for wallet updates
connectWallet(wallet)

Expand Down Expand Up @@ -585,23 +608,79 @@ ApplicationWindow {
devicePassphraseDialog.open(on_device)
}

function onWalletUpdate() {
function onWalletUpdate(stoppedBackgroundSync) {
if (!currentWallet)
return;

console.log(">>> wallet updated")
updateBalance();
// Update history if new block found since last update
if(foundNewBlock) {
// Update history if new block found since last update or background sync was just stopped
if(foundNewBlock || stoppedBackgroundSync) {
if (foundNewBlock)
console.log("New block found - updating history")
foundNewBlock = false;
console.log("New block found - updating history")
currentWallet.history.refresh(currentWallet.currentSubaddressAccount)

if(middlePanel.state == "History")
middlePanel.historyView.update();
}
}

function onBackgroundSyncSetup() {
console.log(">>> background sync setup");
hideProcessingSplash();
if (currentWallet.status !== Wallet.Status_Ok) {
console.error("Error setting up background sync: ", currentWallet.errorString);
appWindow.showStatusMessage(currentWallet.errorString, 5);
return;
}
appWindow.backgroundSyncType = currentWallet.getBackgroundSyncType();
}

function onBackgroundSyncStarted() {
console.log(">>> background sync started");
hideProcessingSplash();
var started = currentWallet.status === Wallet.Status_Ok;
if (!started) {
console.error("Error starting background sync: ", currentWallet.errorString);
appWindow.showStatusMessage(currentWallet.errorString, 5);
}

passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
passwordDialog.onAcceptedCallback = function() {
if(walletPassword === passwordDialog.password) {
if (currentWallet && started) {
appWindow.showProcessingSplash(qsTr("Unlocking..."));
currentWallet.stopBackgroundSync(walletPassword);
} else {
passwordDialog.close();
}
} else {
passwordDialog.showError(qsTr("Wrong password") + translationManager.emptyString);
}
}
passwordDialog.open(usefulName(persistentSettings.wallet_path), "", "", "", started);
}

function onBackgroundSyncStopped() {
console.log(">>> background sync stopped");
var stopped = currentWallet.status === Wallet.Status_Ok;
if (!stopped) {
hideProcessingSplash();
console.error("Error stopping background sync: ", currentWallet.errorString);

// If there is an error stopping background sync, the spend key
// won't be loaded and the wallet will be in a broken state. Don't
// let the user continue normal wallet operations in this state.
passwordDialog.showError(qsTr("Error stopping background sync: ") + currentWallet.errorString);
return;
}

onWalletUpdate(stopped);
hideProcessingSplash();
passwordDialog.close();
}

function connectRemoteNode() {
console.log("connecting remote node");

Expand Down Expand Up @@ -2258,6 +2337,20 @@ ApplicationWindow {
var inactivity = Utils.epoch() - appWindow.userLastActive;
if(inactivity < (persistentSettings.lockOnUserInActivityInterval * 60)) return;

if (inputDialogVisible) inputDialog.close()
remoteNodeDialog.close();
informationPopup.close()
txConfirmationPopup.close()
txConfirmationPopup.clearFields()
txConfirmationPopup.rejected()
successfulTxPopup.close();

if (currentWallet && currentWallet.getBackgroundSyncType() != Wallet.BackgroundSync_Off) {
appWindow.showProcessingSplash(qsTr("Locking..."));
currentWallet.startBackgroundSync()
return;
}

passwordDialog.onAcceptedCallback = function() {
if(walletPassword === passwordDialog.password){
passwordDialog.close();
Expand All @@ -2270,13 +2363,6 @@ ApplicationWindow {
}

passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
if (inputDialogVisible) inputDialog.close()
remoteNodeDialog.close();
informationPopup.close()
txConfirmationPopup.close()
txConfirmationPopup.clearFields()
txConfirmationPopup.rejected()
successfulTxPopup.close();
passwordDialog.open();
}

Expand Down
24 changes: 24 additions & 0 deletions pages/settings/SettingsLayout.qml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2

import moneroComponents.Wallet 1.0

import "../../js/Utils.js" as Utils
import "../../js/Windows.js" as Windows
import "../../components" as MoneroComponents
Expand Down Expand Up @@ -155,6 +157,28 @@ Rectangle {
onMoved: persistentSettings.lockOnUserInActivityInterval = value
}

MoneroComponents.CheckBox {
id: backgroundSyncCheckbox
visible: !!currentWallet && !currentWallet.isHwBacked() && !appWindow.viewOnly
checked: appWindow.backgroundSyncType != Wallet.BackgroundSync_Off
text: qsTr("Sync in the background when locked") + translationManager.emptyString
toggleOnClick: false
onClicked: {
if (currentWallet && appWindow) {
appWindow.showProcessingSplash(qsTr("Updating settings..."))

// TODO: add support for custom background password option
var newBackgroundSyncType = Wallet.BackgroundSync_Off
if (currentWallet.getBackgroundSyncType() === Wallet.BackgroundSync_Off)
newBackgroundSyncType = Wallet.BackgroundSync_ReusePassword

// TODO: don't keep the wallet password in memory on the appWindow
// https://github.com/monero-project/monero-gui/issues/1537#issuecomment-410055329
currentWallet.setupBackgroundSync(newBackgroundSyncType, appWindow.walletPassword)
}
}
}

MoneroComponents.CheckBox {
checked: persistentSettings.askStopLocalNode
onClicked: persistentSettings.askStopLocalNode = !persistentSettings.askStopLocalNode
Expand Down
58 changes: 58 additions & 0 deletions src/libwalletqt/Wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,64 @@ bool Wallet::scanTransactions(const QVector<QString> &txids)
return m_walletImpl->scanTransactions(c);
}

void Wallet::setupBackgroundSync(const Wallet::BackgroundSyncType background_sync_type, const QString &wallet_password)
{
qDebug() << "Setting up background sync";
bool refreshEnabled = m_refreshEnabled;
pauseRefresh();

// run inside scheduler because of lag when stopping/starting refresh
m_scheduler.run([this, refreshEnabled, background_sync_type, &wallet_password] {
m_walletImpl->setupBackgroundSync(
static_cast<Monero::Wallet::BackgroundSyncType>(background_sync_type),
wallet_password.toStdString(),
Monero::optional<std::string>());
if (refreshEnabled)
startRefresh();
emit backgroundSyncSetup();
});
}

Wallet::BackgroundSyncType Wallet::getBackgroundSyncType() const
{
return static_cast<BackgroundSyncType>(m_walletImpl->getBackgroundSyncType());
}

bool Wallet::isBackgroundWallet() const
{
return m_walletImpl->isBackgroundWallet();
}

void Wallet::startBackgroundSync()
{
qDebug() << "Starting background sync";
bool refreshEnabled = m_refreshEnabled;
pauseRefresh();

// run inside scheduler because of lag when stopping/starting refresh
m_scheduler.run([this, refreshEnabled] {
m_walletImpl->startBackgroundSync();
if (refreshEnabled)
startRefresh();
emit backgroundSyncStarted();
});
}

void Wallet::stopBackgroundSync(const QString &password)
{
qDebug() << "Stopping background sync";
bool refreshEnabled = m_refreshEnabled;
pauseRefresh();

// run inside scheduler because of lag when stopping/starting refresh
m_scheduler.run([this, password, refreshEnabled] {
m_walletImpl->stopBackgroundSync(password.toStdString());
if (refreshEnabled)
startRefresh();
emit backgroundSyncStopped();
});
}

bool Wallet::refresh(bool historyAndSubaddresses /* = true */)
{
refreshingSet(true);
Expand Down
21 changes: 21 additions & 0 deletions src/libwalletqt/Wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ class Wallet : public QObject, public PassprasePrompter

Q_ENUM(ConnectionStatus)

enum BackgroundSyncType {
BackgroundSync_Off = Monero::Wallet::BackgroundSync_Off,
BackgroundSync_ReusePassword = Monero::Wallet::BackgroundSync_ReusePassword,
BackgroundSync_CustomPassword = Monero::Wallet::BackgroundSync_CustomPassword
};

Q_ENUM(BackgroundSyncType)

//! returns mnemonic seed
QString getSeed() const;

Expand Down Expand Up @@ -215,6 +223,16 @@ class Wallet : public QObject, public PassprasePrompter
//! scan transactions
Q_INVOKABLE bool scanTransactions(const QVector<QString> &txids);

Q_INVOKABLE void setupBackgroundSync(const BackgroundSyncType background_sync_type, const QString &wallet_password);
Q_INVOKABLE BackgroundSyncType getBackgroundSyncType() const;
Q_INVOKABLE bool isBackgroundWallet() const;

//! scan in the background with just the view key (wipe the spend key)
Q_INVOKABLE void startBackgroundSync();

//! bring the spend key back and process background synced txs
Q_INVOKABLE void stopBackgroundSync(const QString &password);

//! refreshes the wallet
Q_INVOKABLE bool refresh(bool historyAndSubaddresses = true);

Expand Down Expand Up @@ -369,6 +387,9 @@ class Wallet : public QObject, public PassprasePrompter
void moneyReceived(const QString &txId, quint64 amount);
void unconfirmedMoneyReceived(const QString &txId, quint64 amount);
void newBlock(quint64 height, quint64 targetHeight);
void backgroundSyncSetup() const;
void backgroundSyncStarted() const;
void backgroundSyncStopped() const;
void addressBookChanged() const;
void historyModelChanged() const;
void walletCreationHeightChanged();
Expand Down
Loading