Skip to content

Commit

Permalink
Implement background sync when locked
Browse files Browse the repository at this point in the history
  • Loading branch information
j-berman committed Nov 17, 2023
1 parent e9cd458 commit bb6b7f5
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 15 deletions.
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
}
}
}
}
111 changes: 97 additions & 14 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,76 @@ ApplicationWindow {
devicePassphraseDialog.open(on_device)
}

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

function onWalletUpdate(stoppedBackgroundSync) {
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 +2334,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 +2360,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

0 comments on commit bb6b7f5

Please sign in to comment.