aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorj-berman <justinberman@protonmail.com>2023-11-16 16:38:52 -0800
committerj-berman <justinberman@protonmail.com>2025-08-03 10:26:11 -0700
commit5332495c2404ab4b102c98c22419e70143b1dc90 (patch)
tree71c5a543eed50a0c244594afe3fb9028b5650dd4
parent10a184db8b242d4cff97e0f5845342cfccfc2042 (diff)
downloadmonzero-gui-5332495c2404ab4b102c98c22419e70143b1dc90.tar.gz
monzero-gui-5332495c2404ab4b102c98c22419e70143b1dc90.tar.xz
monzero-gui-5332495c2404ab4b102c98c22419e70143b1dc90.zip
Implement background sync when locked
-rw-r--r--components/PasswordDialog.qml18
-rw-r--r--main.qml108
-rw-r--r--pages/History.qml1
-rw-r--r--pages/settings/SettingsLayout.qml24
-rw-r--r--src/libwalletqt/Wallet.cpp63
-rw-r--r--src/libwalletqt/Wallet.h22
6 files changed, 224 insertions, 12 deletions
diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml
index 4de44ae5..77b16658 100644
--- a/components/PasswordDialog.qml
+++ b/components/PasswordDialog.qml
@@ -50,6 +50,7 @@ Item {
property bool passwordDialogMode
property bool passphraseDialogMode
property bool newPasswordDialogMode
+ property bool backgroundSyncing
// same signals as Dialog has
signal accepted()
@@ -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);
@@ -90,6 +92,7 @@ Item {
passwordDialogMode = false;
passphraseDialogMode = true;
newPasswordDialogMode = false;
+ backgroundSyncing = false;
_openInit("", "");
}
@@ -97,6 +100,7 @@ Item {
passwordDialogMode = false;
passphraseDialogMode = false;
newPasswordDialogMode = true;
+ backgroundSyncing = false;
_openInit("", "");
}
@@ -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
+ }
}
}
}
diff --git a/main.qml b/main.qml
index ffbf014e..16fcff21 100644
--- a/main.qml
+++ b/main.qml
@@ -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
@@ -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)
@@ -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)
@@ -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) {
@@ -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)
@@ -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)
@@ -585,16 +608,17 @@ 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")
@@ -602,6 +626,61 @@ ApplicationWindow {
}
}
+ 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");
@@ -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();
@@ -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();
}
diff --git a/pages/History.qml b/pages/History.qml
index 651a6d02..d9132e3b 100644
--- a/pages/History.qml
+++ b/pages/History.qml
@@ -1542,6 +1542,7 @@ Rectangle {
function updateTransactionsFromModel() {
// This function copies the items of `appWindow.currentWallet.historyModel` to `root.txModelData`, as a list of javascript objects
if(currentWallet == null || typeof currentWallet.history === "undefined" ) return;
+ if(currentWallet.isBackgroundSyncing()) return;
var _model = root.model;
var total = 0
diff --git a/pages/settings/SettingsLayout.qml b/pages/settings/SettingsLayout.qml
index c2dfc7e6..e73355ae 100644
--- a/pages/settings/SettingsLayout.qml
+++ b/pages/settings/SettingsLayout.qml
@@ -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
@@ -156,6 +158,28 @@ Rectangle {
}
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
text: qsTr("Ask to stop local node during program exit") + translationManager.emptyString
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index cec4ff63..a5ac6925 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -529,6 +529,69 @@ 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();
+}
+
+bool Wallet::isBackgroundSyncing() const
+{
+ return m_walletImpl->isBackgroundSyncing();
+}
+
+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);
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index 398d19c6..a5bb913c 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -112,6 +112,14 @@ public:
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;
@@ -215,6 +223,17 @@ public:
//! 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;
+ Q_INVOKABLE bool isBackgroundSyncing() 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);
@@ -369,6 +388,9 @@ signals:
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();