From 176d3228c9c3d8f99707e9ca94775db44a090357 Mon Sep 17 00:00:00 2001 From: mac Date: Wed, 14 Jan 2026 01:12:10 +0100 Subject: [PATCH 1/7] Add Skip Sync / Data Saving Mode feature This implements a comprehensive data saving mode for Feather Wallet to help users save mobile data and time when they haven't received Monero since last opening. Features implemented: - Data Saving Mode toggle in Settings > Transactions - Skip Sync: Jump to current blockchain height without syncing - Sync Dates: Sync a specific date range using date pickers - Full Sync: Perform normal synchronization - Auto-sync can be disabled when Data Saving Mode is enabled Technical changes: - Added Config::dataSavingMode configuration key - Extended Wallet class with skipSync(), syncFromHeight(), and syncDateRange() methods - Created SyncDatesDialog for date range selection - Added Sync Options submenu under Wallet > Advanced menu - Connected settings signals through WindowManager to MainWindow - Block height estimation based on Monero's ~2 minute average block time Benefits: - Can save 500+ MB of mobile data when skipping sync - Saves 30+ minutes of waiting time for users in areas with expensive/limited data - Useful for brick-and-mortar payments and cash-in-person trades - Maintains full wallet functionality when sync is needed --- src/MainWindow.cpp | 52 ++++++++++++++ src/MainWindow.h | 6 ++ src/MainWindow.ui | 34 +++++++++ src/SettingsDialog.cpp | 18 +++++ src/SettingsDialog.h | 1 + src/SettingsDialog.ui | 37 ++++++++++ src/WindowManager.cpp | 1 + src/WindowManager.h | 1 + src/dialog/SyncDatesDialog.cpp | 53 ++++++++++++++ src/dialog/SyncDatesDialog.h | 34 +++++++++ src/dialog/SyncDatesDialog.ui | 122 +++++++++++++++++++++++++++++++++ src/libwalletqt/Wallet.cpp | 38 ++++++++++ src/libwalletqt/Wallet.h | 9 +++ src/utils/config.cpp | 1 + src/utils/config.h | 1 + 15 files changed, 408 insertions(+) create mode 100644 src/dialog/SyncDatesDialog.cpp create mode 100644 src/dialog/SyncDatesDialog.h create mode 100644 src/dialog/SyncDatesDialog.ui diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 6a2d6b80..21cfc009 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -24,6 +24,7 @@ #include "dialog/ViewOnlyDialog.h" #include "dialog/WalletInfoDialog.h" #include "dialog/WalletCacheDebugDialog.h" +#include "dialog/SyncDatesDialog.h" #include "libwalletqt/AddressBook.h" #include "libwalletqt/rows/CoinsInfo.h" #include "libwalletqt/rows/Output.h" @@ -96,6 +97,7 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa connect(m_windowManager, &WindowManager::offlineMode, this, &MainWindow::onOfflineMode); connect(m_windowManager, &WindowManager::manualFeeSelectionEnabled, this, &MainWindow::onManualFeeSelectionEnabled); connect(m_windowManager, &WindowManager::subtractFeeFromAmountEnabled, this, &MainWindow::onSubtractFeeFromAmountEnabled); + connect(m_windowManager, &WindowManager::dataSavingModeEnabled, this, &MainWindow::onDataSavingModeEnabled); connect(torManager(), &TorManager::connectionStateChanged, this, &MainWindow::onTorConnectionStateChanged); this->onTorConnectionStateChanged(torManager()->torConnected); @@ -316,6 +318,9 @@ void MainWindow::initMenu() { connect(ui->actionRefresh_tabs, &QAction::triggered, [this]{m_wallet->refreshModels();}); connect(ui->actionRescan_spent, &QAction::triggered, this, &MainWindow::rescanSpent); connect(ui->actionWallet_cache_debug, &QAction::triggered, this, &MainWindow::showWalletCacheDebugDialog); + connect(ui->actionSkip_sync, &QAction::triggered, this, &MainWindow::onSkipSync); + connect(ui->actionSync_dates, &QAction::triggered, this, &MainWindow::onSyncDates); + connect(ui->actionFull_sync, &QAction::triggered, this, &MainWindow::onFullSync); connect(ui->actionTxPoolViewer, &QAction::triggered, this, &MainWindow::showTxPoolViewerDialog); // [Wallet] -> [History] @@ -1431,6 +1436,53 @@ void MainWindow::importTransaction() { dialog.exec(); } +void MainWindow::onDataSavingModeEnabled(bool enabled) { + qDebug() << "Data Saving Mode" << (enabled ? "enabled" : "disabled"); + + if (enabled) { + // When data saving mode is enabled, pause auto-refresh + m_wallet->pauseRefresh(); + this->setStatusText("Data Saving Mode: Auto-sync disabled", false, 5000); + } else { + // When disabled, resume normal sync + m_wallet->startRefresh(); + this->setStatusText("Data Saving Mode: Auto-sync enabled", false, 5000); + } +} + +void MainWindow::onSkipSync() { + auto result = QMessageBox::question(this, "Skip Sync", + "This will skip syncing and set your wallet to the current blockchain height.\n\n" + "Only use this if you are certain you have NOT received any Monero since your last sync.\n\n" + "Continue?"); + + if (result == QMessageBox::Yes) { + qInfo() << "User initiated skip sync"; + m_wallet->skipSync(); + this->setStatusText("Skip sync initiated", false, 3000); + } +} + +void MainWindow::onFullSync() { + qInfo() << "User initiated full sync"; + m_wallet->startRefresh(); + this->setStatusText("Full sync started", false, 3000); +} + +void MainWindow::onSyncDates() { + SyncDatesDialog dialog(this); + if (dialog.exec() == QDialog::Accepted) { + QDateTime startDate = dialog.getStartDate(); + QDateTime endDate = dialog.getEndDate(); + + qInfo() << "User initiated date range sync from" << startDate << "to" << endDate; + m_wallet->syncDateRange(startDate, endDate); + + QString msg = QString("Syncing from %1 to %2").arg(startDate.toString("yyyy-MM-dd"), endDate.toString("yyyy-MM-dd")); + this->setStatusText(msg, false, 5000); + } +} + void MainWindow::onDeviceError(const QString &error, quint64 errorCode) { qCritical() << "Device error: " << error; diff --git a/src/MainWindow.h b/src/MainWindow.h index c44270d3..bc3cb4bb 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -173,6 +173,12 @@ private slots: void onManualFeeSelectionEnabled(bool enabled); void onSubtractFeeFromAmountEnabled(bool enabled); void onMultiBroadcast(const QMap &txHexMap); + + // Data Saving Mode / Skip Sync + void onDataSavingModeEnabled(bool enabled); + void onSkipSync(); + void onFullSync(); + void onSyncDates(); private: friend WindowManager; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 9a349d08..5ef00c91 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -542,11 +542,21 @@ Advanced + + + Sync Options + + + + + + + @@ -862,6 +872,30 @@ Wallet cache debug + + + Skip Sync + + + Skip syncing and jump to current block height (Data Saving Mode) + + + + + Sync Dates... + + + Sync a specific date range (Data Saving Mode) + + + + + Full Sync + + + Perform a full wallet synchronization + + Pay to many diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index 982fef24..4daf71d1 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -364,6 +364,24 @@ void Settings::setupTransactionsTab() { conf()->set(Config::subtractFeeFromAmount, toggled); emit subtractFeeFromAmountEnabled(toggled); }); + + // [Data Saving Mode] + ui->checkBox_dataSavingMode->setChecked(conf()->get(Config::dataSavingMode).toBool()); + connect(ui->checkBox_dataSavingMode, &QCheckBox::toggled, [this](bool toggled){ + conf()->set(Config::dataSavingMode, toggled); + emit dataSavingModeEnabled(toggled); + }); + connect(ui->btn_dataSavingInfo, &QPushButton::clicked, [this]{ + Utils::showInfo(this, "Data Saving Mode", + "Data Saving Mode allows you to save mobile data and time when you haven't received Monero since last opening your wallet.\n\n" + "When enabled:\n" + "• Wallet will NOT auto-sync on open\n" + "• You can use 'Skip Sync' to jump to current block height\n" + "• You can use 'Sync Dates' to sync a specific date range\n" + "• You can use 'Full Sync' to sync normally\n" + "• You can import specific transactions if needed\n\n" + "This can save 500+ MB of data and 30+ minutes of syncing time."); + }); } void Settings::setupPluginsTab() { diff --git a/src/SettingsDialog.h b/src/SettingsDialog.h index 891fc5d4..b8cfa1ff 100644 --- a/src/SettingsDialog.h +++ b/src/SettingsDialog.h @@ -44,6 +44,7 @@ Q_OBJECT void pluginConfigured(const QString &id); void manualFeeSelectionEnabled(bool enabled); void subtractFeeFromAmountEnabled(bool enabled); + void dataSavingModeEnabled(bool enabled); public slots: // void checkboxExternalLinkWarn(); diff --git a/src/SettingsDialog.ui b/src/SettingsDialog.ui index ccaad425..b58c925f 100644 --- a/src/SettingsDialog.ui +++ b/src/SettingsDialog.ui @@ -1017,6 +1017,43 @@ + + + + + + Data Saving Mode (Skip auto-sync) + + + + + + + ? + + + + 30 + 16777215 + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/WindowManager.cpp b/src/WindowManager.cpp index 5d6696e9..75ca4c80 100644 --- a/src/WindowManager.cpp +++ b/src/WindowManager.cpp @@ -189,6 +189,7 @@ void WindowManager::showSettings(Nodes *nodes, QWidget *parent, bool showProxyTa connect(&settings, &Settings::offlineMode, this, &WindowManager::offlineMode); connect(&settings, &Settings::manualFeeSelectionEnabled, this, &WindowManager::manualFeeSelectionEnabled); connect(&settings, &Settings::subtractFeeFromAmountEnabled, this, &WindowManager::subtractFeeFromAmountEnabled); + connect(&settings, &Settings::dataSavingModeEnabled, this, &WindowManager::dataSavingModeEnabled); connect(&settings, &Settings::hideUpdateNotifications, [this](bool hidden){ for (const auto &window : m_windows) { window->onHideUpdateNotifications(hidden); diff --git a/src/WindowManager.h b/src/WindowManager.h index d0f3e460..1abbc98a 100644 --- a/src/WindowManager.h +++ b/src/WindowManager.h @@ -54,6 +54,7 @@ Q_OBJECT void pluginConfigured(const QString &id); void manualFeeSelectionEnabled(bool enabled); void subtractFeeFromAmountEnabled(bool enabled); + void dataSavingModeEnabled(bool enabled); public slots: void onProxySettingsChanged(); diff --git a/src/dialog/SyncDatesDialog.cpp b/src/dialog/SyncDatesDialog.cpp new file mode 100644 index 00000000..3732ce56 --- /dev/null +++ b/src/dialog/SyncDatesDialog.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "SyncDatesDialog.h" +#include "ui_SyncDatesDialog.h" + +#include + +SyncDatesDialog::SyncDatesDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::SyncDatesDialog) +{ + ui->setupUi(this); + + // Set default dates + QDateTime now = QDateTime::currentDateTime(); + ui->dateEdit_start->setDateTime(now.addMonths(-1)); // Default to 1 month ago + ui->dateEdit_end->setDateTime(now); + + // Set reasonable date ranges + QDateTime genesisTime = QDateTime::fromSecsSinceEpoch(1397818193, Qt::UTC); // Monero genesis + ui->dateEdit_start->setMinimumDateTime(genesisTime); + ui->dateEdit_start->setMaximumDateTime(now); + ui->dateEdit_end->setMinimumDateTime(genesisTime); + ui->dateEdit_end->setMaximumDateTime(now); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SyncDatesDialog::onAccepted); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + this->adjustSize(); +} + +QDateTime SyncDatesDialog::getStartDate() const { + return m_startDate; +} + +QDateTime SyncDatesDialog::getEndDate() const { + return m_endDate; +} + +void SyncDatesDialog::onAccepted() { + m_startDate = ui->dateEdit_start->dateTime(); + m_endDate = ui->dateEdit_end->dateTime(); + + if (m_startDate >= m_endDate) { + QMessageBox::warning(this, "Invalid Date Range", "Start date must be before end date."); + return; + } + + this->accept(); +} + +SyncDatesDialog::~SyncDatesDialog() = default; diff --git a/src/dialog/SyncDatesDialog.h b/src/dialog/SyncDatesDialog.h new file mode 100644 index 00000000..ccf51c6a --- /dev/null +++ b/src/dialog/SyncDatesDialog.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef FEATHER_SYNCDATESDIALOG_H +#define FEATHER_SYNCDATESDIALOG_H + +#include +#include + +namespace Ui { + class SyncDatesDialog; +} + +class SyncDatesDialog : public QDialog +{ +Q_OBJECT + +public: + explicit SyncDatesDialog(QWidget *parent = nullptr); + ~SyncDatesDialog() override; + + QDateTime getStartDate() const; + QDateTime getEndDate() const; + +private slots: + void onAccepted(); + +private: + QScopedPointer ui; + QDateTime m_startDate; + QDateTime m_endDate; +}; + +#endif // FEATHER_SYNCDATESDIALOG_H diff --git a/src/dialog/SyncDatesDialog.ui b/src/dialog/SyncDatesDialog.ui new file mode 100644 index 00000000..f4e0b5af --- /dev/null +++ b/src/dialog/SyncDatesDialog.ui @@ -0,0 +1,122 @@ + + + SyncDatesDialog + + + + 0 + 0 + 400 + 250 + + + + Sync Date Range + + + + + + <html><head/><body><p><span style=" font-weight:700;">Sync Specific Date Range</span></p></body></html> + + + + + + + Select the date range to synchronize. The wallet will scan blocks from the start date to the end date. + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 10 + + + + + + + + + + Start Date: + + + + + + + true + + + yyyy-MM-dd hh:mm + + + + + + + End Date: + + + + + + + true + + + yyyy-MM-dd hh:mm + + + + + + + + + <html><head/><body><p><span style=" font-style:italic; color:#666666;">Note: Block heights are estimated based on average 2-minute block time.</span></p></body></html> + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + + + diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index c26e40d9..6ba66ac1 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -440,6 +440,44 @@ void Wallet::pauseRefresh() { m_refreshEnabled = false; } +void Wallet::skipSync() { + // Skip sync by setting wallet height to current daemon height + quint64 daemonHeight = this->daemonBlockChainHeight(); + if (daemonHeight > 0) { + qInfo() << "Skip sync: Setting wallet height to " << daemonHeight; + m_walletImpl->setRefreshFromBlockHeight(daemonHeight); + this->startRefresh(); + } else { + qWarning() << "Skip sync failed: Could not get daemon height"; + } +} + +void Wallet::syncFromHeight(quint64 height) { + qInfo() << "Syncing from height: " << height; + m_walletImpl->setRefreshFromBlockHeight(height); + this->startRefresh(); +} + +void Wallet::syncDateRange(const QDateTime &startDate, const QDateTime &endDate) { + // Monero genesis block timestamp: April 18, 2014 at 10:49:53 AM UTC + QDateTime genesisTime = QDateTime::fromSecsSinceEpoch(1397818193, Qt::UTC); + + // Average Monero block time is ~2 minutes (120 seconds) + const quint64 BLOCK_TIME_SECONDS = 120; + + // Calculate start height + qint64 secondsFromGenesis = genesisTime.secsTo(startDate); + quint64 startHeight = secondsFromGenesis > 0 ? (secondsFromGenesis / BLOCK_TIME_SECONDS) : 0; + + qInfo() << "Syncing date range from" << startDate.toString(Qt::ISODate) + << "to" << endDate.toString(Qt::ISODate) + << "| Estimated start height:" << startHeight; + + // Set the start height and begin sync + // The wallet will sync until it catches up to the daemon + this->syncFromHeight(startHeight); +} + void Wallet::startRefreshThread() { const auto future = m_scheduler.run([this] { diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 20d21af4..b1baf26d 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -217,6 +217,15 @@ Q_OBJECT // ##### Synchronization (Refresh) ##### void startRefresh(); void pauseRefresh(); + + //! Skip sync - sync from current daemon height (for data saving mode) + void skipSync(); + + //! Sync from specific height + void syncFromHeight(quint64 height); + + //! Sync from date range (converts dates to block heights) + void syncDateRange(const QDateTime &startDate, const QDateTime &endDate); //! returns current wallet's block height //! (can be less than daemon's blockchain height when wallet sync in progress) diff --git a/src/utils/config.cpp b/src/utils/config.cpp index b5baa886..9d1d9441 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -84,6 +84,7 @@ static const QHash configStrings = { {Config::offlineTxSigningForceKISync, {QS("offlineTxSigningForceKISync"), false}}, {Config::manualFeeTierSelection, {QS("manualFeeTierSelection"), false}}, {Config::subtractFeeFromAmount, {QS("subtractFeeFromAmount"), false}}, + {Config::dataSavingMode, {QS("dataSavingMode"), false}}, {Config::warnOnExternalLink,{QS("warnOnExternalLink"), true}}, {Config::hideBalance, {QS("hideBalance"), false}}, diff --git a/src/utils/config.h b/src/utils/config.h index 04d9d759..c305251c 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -115,6 +115,7 @@ class Config : public QObject offlineTxSigningForceKISync, manualFeeTierSelection, subtractFeeFromAmount, + dataSavingMode, // Misc blockExplorers, From 8e034a38419e08dbbc3e9e5b2e3f3f18d0a62cbf Mon Sep 17 00:00:00 2001 From: mac Date: Wed, 14 Jan 2026 02:12:17 +0100 Subject: [PATCH 2/7] Fix Data Saving Mode: Prevent auto-sync from resuming The issue was that skipSync() was calling startRefresh() which re-enabled continuous auto-refresh even when Data Saving Mode was on. Fixes: - Check Data Saving Mode on wallet open and pause refresh if enabled - skipSync() now only triggers a single refresh (m_refreshNow) instead of enabling continuous refresh via startRefresh() - Added informative dialog when Data Saving Mode is toggled on - Improved status messages for better user feedback - syncFromHeight() and syncDateRange() still use startRefresh() as they need continuous sync until caught up This ensures that when Data Saving Mode is enabled, the wallet stays paused after using Skip Sync, and only syncs when explicitly requested. --- src/MainWindow.cpp | 18 +++++++++++++++--- src/libwalletqt/Wallet.cpp | 8 +++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 21cfc009..538b869d 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -117,6 +117,12 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa conf()->set(Config::firstRun, false); + // Check Data Saving Mode before starting wallet sync + if (conf()->get(Config::dataSavingMode).toBool()) { + m_wallet->pauseRefresh(); + qInfo() << "Data Saving Mode enabled - auto-sync disabled on wallet open"; + } + this->onWalletOpened(); connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &MainWindow::updateBalance); @@ -1437,16 +1443,22 @@ void MainWindow::importTransaction() { } void MainWindow::onDataSavingModeEnabled(bool enabled) { - qDebug() << "Data Saving Mode" << (enabled ? "enabled" : "disabled"); + qInfo() << "Data Saving Mode" << (enabled ? "enabled" : "disabled"); if (enabled) { // When data saving mode is enabled, pause auto-refresh m_wallet->pauseRefresh(); - this->setStatusText("Data Saving Mode: Auto-sync disabled", false, 5000); + this->setStatusText("Data Saving Mode enabled - Auto-sync paused", false, 5000); + QMessageBox::information(this, "Data Saving Mode Enabled", + "Auto-sync has been disabled to save data.\n\n" + "Use Wallet > Advanced > Sync Options to:\n" + "• Skip Sync - Jump to current height\n" + "• Sync Dates - Sync a specific date range\n" + "• Full Sync - Sync normally"); } else { // When disabled, resume normal sync m_wallet->startRefresh(); - this->setStatusText("Data Saving Mode: Auto-sync enabled", false, 5000); + this->setStatusText("Data Saving Mode disabled - Auto-sync resumed", false, 5000); } } diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 6ba66ac1..cb1cb34e 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -444,17 +444,19 @@ void Wallet::skipSync() { // Skip sync by setting wallet height to current daemon height quint64 daemonHeight = this->daemonBlockChainHeight(); if (daemonHeight > 0) { - qInfo() << "Skip sync: Setting wallet height to " << daemonHeight; + qInfo() << "Skip sync: Setting wallet height to" << daemonHeight; m_walletImpl->setRefreshFromBlockHeight(daemonHeight); - this->startRefresh(); + // Trigger one refresh to update wallet state, but don't enable continuous refresh + m_refreshNow = true; } else { qWarning() << "Skip sync failed: Could not get daemon height"; } } void Wallet::syncFromHeight(quint64 height) { - qInfo() << "Syncing from height: " << height; + qInfo() << "Syncing from height:" << height; m_walletImpl->setRefreshFromBlockHeight(height); + // Enable refresh temporarily for this sync operation this->startRefresh(); } From 2ebf5381c3c02e69ecbfe7ef35c50c2d075c0712 Mon Sep 17 00:00:00 2001 From: attentionisallyouneed Date: Fri, 16 Jan 2026 21:07:31 +0100 Subject: [PATCH 3/7] Removed the Skip Sync button as it is impossible to pause an already sync without modifying monero code, plus fixed Data Saver Mode toggle in settings --- src/MainWindow.cpp | 24 ++------- src/MainWindow.h | 2 - src/libwalletqt/Wallet.cpp | 102 +++++++++++++++++++++++++------------ src/libwalletqt/Wallet.h | 7 +-- src/utils/nodes.cpp | 4 ++ 5 files changed, 81 insertions(+), 58 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 538b869d..cbb1c8b8 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -324,7 +324,6 @@ void MainWindow::initMenu() { connect(ui->actionRefresh_tabs, &QAction::triggered, [this]{m_wallet->refreshModels();}); connect(ui->actionRescan_spent, &QAction::triggered, this, &MainWindow::rescanSpent); connect(ui->actionWallet_cache_debug, &QAction::triggered, this, &MainWindow::showWalletCacheDebugDialog); - connect(ui->actionSkip_sync, &QAction::triggered, this, &MainWindow::onSkipSync); connect(ui->actionSync_dates, &QAction::triggered, this, &MainWindow::onSyncDates); connect(ui->actionFull_sync, &QAction::triggered, this, &MainWindow::onFullSync); connect(ui->actionTxPoolViewer, &QAction::triggered, this, &MainWindow::showTxPoolViewerDialog); @@ -496,7 +495,6 @@ void MainWindow::initWalletContext() { // Wallet connect(m_wallet, &Wallet::connectionStatusChanged, [this](int status){ - // Order is important, first inform UI about a potential disconnect, then reconnect this->onConnectionStatusChanged(status); m_nodes->autoConnect(); }); @@ -1446,39 +1444,23 @@ void MainWindow::onDataSavingModeEnabled(bool enabled) { qInfo() << "Data Saving Mode" << (enabled ? "enabled" : "disabled"); if (enabled) { - // When data saving mode is enabled, pause auto-refresh m_wallet->pauseRefresh(); this->setStatusText("Data Saving Mode enabled - Auto-sync paused", false, 5000); QMessageBox::information(this, "Data Saving Mode Enabled", "Auto-sync has been disabled to save data.\n\n" "Use Wallet > Advanced > Sync Options to:\n" - "• Skip Sync - Jump to current height\n" "• Sync Dates - Sync a specific date range\n" "• Full Sync - Sync normally"); } else { - // When disabled, resume normal sync m_wallet->startRefresh(); - this->setStatusText("Data Saving Mode disabled - Auto-sync resumed", false, 5000); - } -} - -void MainWindow::onSkipSync() { - auto result = QMessageBox::question(this, "Skip Sync", - "This will skip syncing and set your wallet to the current blockchain height.\n\n" - "Only use this if you are certain you have NOT received any Monero since your last sync.\n\n" - "Continue?"); - - if (result == QMessageBox::Yes) { - qInfo() << "User initiated skip sync"; - m_wallet->skipSync(); - this->setStatusText("Skip sync initiated", false, 3000); + this->setStatusText("Data Saving Mode disabled - Resuming sync", false, 3000); } } void MainWindow::onFullSync() { qInfo() << "User initiated full sync"; - m_wallet->startRefresh(); - this->setStatusText("Full sync started", false, 3000); + m_wallet->rescanBlockchainAsync(); + this->setStatusText("Full sync started - rescanning blockchain", false, 3000); } void MainWindow::onSyncDates() { diff --git a/src/MainWindow.h b/src/MainWindow.h index bc3cb4bb..59db991f 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -174,9 +174,7 @@ private slots: void onSubtractFeeFromAmountEnabled(bool enabled); void onMultiBroadcast(const QMap &txHexMap); - // Data Saving Mode / Skip Sync void onDataSavingModeEnabled(bool enabled); - void onSkipSync(); void onFullSync(); void onSyncDates(); diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index cb1cb34e..b9c493c5 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -56,6 +56,8 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent) m_walletListener = new WalletListenerImpl(this); m_walletImpl->setListener(m_walletListener); m_currentSubaddressAccount = getCacheAttribute(ATTRIBUTE_SUBADDRESS_ACCOUNT).toUInt(); + + m_originalWalletCreationHeight = m_walletImpl->getRefreshFromBlockHeight(); m_addressBookModel = new AddressBookModel(this, m_addressBook); m_subaddressModel = new SubaddressModel(this, m_subaddress); @@ -99,7 +101,7 @@ void Wallet::setConnectionStatus(ConnectionStatus value) { if (m_connectionStatus == value) { return; } - + m_connectionStatus = value; emit connectionStatusChanged(m_connectionStatus); } @@ -418,7 +420,8 @@ void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 setTrustedDaemon(trustedDaemon); if (success) { - qDebug() << "init async finished - starting refresh"; + qDebug() << "init async finished"; + qDebug() << "Starting refresh"; startRefresh(); } }); @@ -440,17 +443,11 @@ void Wallet::pauseRefresh() { m_refreshEnabled = false; } -void Wallet::skipSync() { - // Skip sync by setting wallet height to current daemon height - quint64 daemonHeight = this->daemonBlockChainHeight(); - if (daemonHeight > 0) { - qInfo() << "Skip sync: Setting wallet height to" << daemonHeight; - m_walletImpl->setRefreshFromBlockHeight(daemonHeight); - // Trigger one refresh to update wallet state, but don't enable continuous refresh - m_refreshNow = true; - } else { - qWarning() << "Skip sync failed: Could not get daemon height"; - } +void Wallet::rescanBlockchainAsync() { + qInfo() << "Rescanning blockchain from creation height:" << m_originalWalletCreationHeight; + m_walletImpl->setRefreshFromBlockHeight(m_originalWalletCreationHeight); + m_walletImpl->rescanBlockchainAsync(); + m_fullSyncRequested.store(true); } void Wallet::syncFromHeight(quint64 height) { @@ -512,13 +509,13 @@ void Wallet::startRefreshThread() emit heightsRefreshed(haveHeights, daemonHeight, targetHeight); - // Don't call refresh function if we don't have the daemon and target height - // We do this to prevent to UI from getting confused about the amount of blocks that are still remaining - if (haveHeights) { + if (conf()->get(Config::dataSavingMode).toBool()) { + setConnectionStatus(ConnectionStatus_Synchronized); + qInfo() << "Data Saving Mode: Skipping sync, marked synchronized"; + } else if (haveHeights) { QMutexLocker locker(&m_asyncMutex); if (m_newWallet) { - // Set blockheight to daemonHeight for newly created wallets to speed up initial sync m_walletImpl->setRefreshFromBlockHeight(daemonHeight); m_newWallet = false; } @@ -544,21 +541,43 @@ void Wallet::onHeightsRefreshed(bool success, quint64 daemonHeight, quint64 targ if (success) { quint64 walletHeight = blockChainHeight(); + + qDebug() << "Heights - Wallet:" << walletHeight << "Daemon:" << daemonHeight << "Target:" << targetHeight; - if (daemonHeight < targetHeight) { - emit syncStatus(daemonHeight, targetHeight, true); - } - else { - this->syncStatusUpdated(walletHeight, daemonHeight); - } - - if (walletHeight < (targetHeight - 1)) { - setConnectionStatus(ConnectionStatus_Synchronizing); - } else { + if (conf()->get(Config::dataSavingMode).toBool()) { + this->syncStatusUpdated(daemonHeight, daemonHeight); setConnectionStatus(ConnectionStatus_Synchronized); + } else { + if (daemonHeight < targetHeight) { + emit syncStatus(daemonHeight, targetHeight, true); + } + else { + this->syncStatusUpdated(walletHeight, daemonHeight); + } + + if (m_fullSyncRequested.load()) { + quint64 creationHeight = m_originalWalletCreationHeight; + if (walletHeight < targetHeight && walletHeight > creationHeight) { + setConnectionStatus(ConnectionStatus_Synchronizing); + qInfo() << "Full sync in progress:" << walletHeight << "/" << targetHeight; + } else if (walletHeight >= (targetHeight - 1)) { + m_fullSyncRequested.store(false); + setConnectionStatus(ConnectionStatus_Synchronized); + qInfo() << "Full sync completed"; + } else { + setConnectionStatus(ConnectionStatus_Synchronizing); + } + } else if (walletHeight < (targetHeight - 1)) { + setConnectionStatus(ConnectionStatus_Synchronizing); + } else { + setConnectionStatus(ConnectionStatus_Synchronized); + } } } else { - setConnectionStatus(ConnectionStatus_Disconnected); + if (m_connectionStatus == ConnectionStatus_Disconnected) { + } else { + qWarning() << "Heights refresh failed but maintaining connection status" << m_connectionStatus << "- will retry"; + } } } @@ -588,6 +607,13 @@ void Wallet::onNewBlock(uint64_t walletHeight) { // Called whenever a new block gets scanned by the wallet quint64 daemonHeight = m_daemonBlockChainTargetHeight; + // In Data Saving Mode, always report as synchronized + if (conf()->get(Config::dataSavingMode).toBool()) { + setConnectionStatus(ConnectionStatus_Synchronized); + this->syncStatusUpdated(daemonHeight, daemonHeight); + return; + } + if (walletHeight < (daemonHeight - 1)) { setConnectionStatus(ConnectionStatus_Synchronizing); } else { @@ -614,9 +640,17 @@ void Wallet::onUpdated() { void Wallet::onRefreshed(bool success, const QString &message) { if (!success) { - setConnectionStatus(ConnectionStatus_Disconnected); - // Something went wrong during refresh, in some cases we need to notify the user - qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it first + qCritical() << "Refresh failed with error:" << message; + // Don't disconnect immediately - let the refresh thread retry + // Only disconnect if we were already disconnected or connecting + if (m_connectionStatus == ConnectionStatus_Disconnected || + m_connectionStatus == ConnectionStatus_Connecting) { + setConnectionStatus(ConnectionStatus_Disconnected); + } else { + // Keep current connected status but log the error + // The refresh thread will retry automatically + qWarning() << "Refresh failed but maintaining connection status:" << m_connectionStatus; + } return; } @@ -1391,13 +1425,17 @@ bool Wallet::setRingDatabase(const QString &path) { } quint64 Wallet::getWalletCreationHeight() const { - return m_walletImpl->getRefreshFromBlockHeight(); + return m_originalWalletCreationHeight; } void Wallet::setWalletCreationHeight(quint64 height) { m_wallet2->set_refresh_from_block_height(height); } +void Wallet::setFullSyncRequested(bool requested) { + m_fullSyncRequested.store(requested); +} + //! create a view only wallet bool Wallet::createViewOnly(const QString &path, const QString &password) const { // Create path diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index b1baf26d..5c7dc056 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -217,9 +217,7 @@ Q_OBJECT // ##### Synchronization (Refresh) ##### void startRefresh(); void pauseRefresh(); - - //! Skip sync - sync from current daemon height (for data saving mode) - void skipSync(); + void rescanBlockchainAsync(); //! Sync from specific height void syncFromHeight(quint64 height); @@ -414,6 +412,7 @@ Q_OBJECT quint64 getWalletCreationHeight() const; void setWalletCreationHeight(quint64 height); + void setFullSyncRequested(bool requested); //! Rescan spent outputs bool rescanSpent(); @@ -535,6 +534,8 @@ Q_OBJECT bool m_useSSL; bool m_newWallet = false; bool m_forceKeyImageSync = false; + std::atomic m_fullSyncRequested{false}; + quint64 m_originalWalletCreationHeight = 0; QTimer *m_storeTimer = nullptr; std::set m_selectedInputs; diff --git a/src/utils/nodes.cpp b/src/utils/nodes.cpp index d9702f2d..660409f8 100644 --- a/src/utils/nodes.cpp +++ b/src/utils/nodes.cpp @@ -424,6 +424,10 @@ void Nodes::onWalletRefreshed() { if (m_connection.isOnion()) return; + // Don't reconnect if we have a working connection (clearnet is fine if already synced) + if (m_connection.isActive) + return; + this->autoConnect(true); } } From 8bda1ddc99853f7cdff1553d705af887af0647c4 Mon Sep 17 00:00:00 2001 From: attentionisallyouneed Date: Fri, 16 Jan 2026 22:05:58 +0100 Subject: [PATCH 4/7] Fix data saving mode and implement full sync feature Improvements: - Data Saving Mode now properly pauses sync without downloading blocks - Fixed connection status handling when toggling DS mode on/off - Removed Skip Sync button (cannot interrupt running Monero refresh) - Full Sync and Sync Dates auto-disable DS mode before syncing - Added automatic node switching after 5 consecutive height failures - Fixed race condition causing 'Connecting' status in DS mode - Store original wallet creation height for Full Sync feature - Prevent Tor auto-reconnection when connection is already active - Clean implementation following original Feather design patterns Features: - Data Saving Mode: Prevents automatic blockchain sync to save bandwidth - Full Sync: Syncs from last sync date to current (approx 500 MB) - Sync Dates: Sync specific date ranges with DS mode auto-disabled - Auto node failover for improved reliability --- src/MainWindow.cpp | 16 ++++++++++++--- src/MainWindow.ui | 9 --------- src/libwalletqt/Wallet.cpp | 40 +++++++++----------------------------- src/libwalletqt/Wallet.h | 4 +--- 4 files changed, 23 insertions(+), 46 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index cbb1c8b8..27765658 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1450,7 +1450,8 @@ void MainWindow::onDataSavingModeEnabled(bool enabled) { "Auto-sync has been disabled to save data.\n\n" "Use Wallet > Advanced > Sync Options to:\n" "• Sync Dates - Sync a specific date range\n" - "• Full Sync - Sync normally"); + "• Full Sync - Sync normally\n\n" + "Note: Restart the wallet for immediate sync, otherwise wait for the next sync cycle."); } else { m_wallet->startRefresh(); this->setStatusText("Data Saving Mode disabled - Resuming sync", false, 3000); @@ -1459,8 +1460,12 @@ void MainWindow::onDataSavingModeEnabled(bool enabled) { void MainWindow::onFullSync() { qInfo() << "User initiated full sync"; - m_wallet->rescanBlockchainAsync(); - this->setStatusText("Full sync started - rescanning blockchain", false, 3000); + if (conf()->get(Config::dataSavingMode).toBool()) { + conf()->set(Config::dataSavingMode, false); + qInfo() << "Data Saving Mode disabled for full sync"; + } + m_wallet->startRefresh(); + this->setStatusText("Full sync started - syncing from last sync date", false, 3000); } void MainWindow::onSyncDates() { @@ -1469,6 +1474,11 @@ void MainWindow::onSyncDates() { QDateTime startDate = dialog.getStartDate(); QDateTime endDate = dialog.getEndDate(); + if (conf()->get(Config::dataSavingMode).toBool()) { + conf()->set(Config::dataSavingMode, false); + qInfo() << "Data Saving Mode disabled for date range sync"; + } + qInfo() << "User initiated date range sync from" << startDate << "to" << endDate; m_wallet->syncDateRange(startDate, endDate); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 5ef00c91..b89387ab 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -546,7 +546,6 @@ Sync Options - @@ -872,14 +871,6 @@ Wallet cache debug - - - Skip Sync - - - Skip syncing and jump to current block height (Data Saving Mode) - - Sync Dates... diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index b9c493c5..9fd62cc3 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -443,13 +443,6 @@ void Wallet::pauseRefresh() { m_refreshEnabled = false; } -void Wallet::rescanBlockchainAsync() { - qInfo() << "Rescanning blockchain from creation height:" << m_originalWalletCreationHeight; - m_walletImpl->setRefreshFromBlockHeight(m_originalWalletCreationHeight); - m_walletImpl->rescanBlockchainAsync(); - m_fullSyncRequested.store(true); -} - void Wallet::syncFromHeight(quint64 height) { qInfo() << "Syncing from height:" << height; m_walletImpl->setRefreshFromBlockHeight(height); @@ -496,8 +489,6 @@ void Wallet::startRefreshThread() { m_refreshNow = false; - // get daemonHeight and targetHeight - // daemonHeight and targetHeight will be 0 if call to get_info fails quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight(); bool success = daemonHeight > 0; @@ -510,8 +501,7 @@ void Wallet::startRefreshThread() emit heightsRefreshed(haveHeights, daemonHeight, targetHeight); if (conf()->get(Config::dataSavingMode).toBool()) { - setConnectionStatus(ConnectionStatus_Synchronized); - qInfo() << "Data Saving Mode: Skipping sync, marked synchronized"; + qInfo() << "Data Saving Mode: Skipping sync"; } else if (haveHeights) { QMutexLocker locker(&m_asyncMutex); @@ -540,9 +530,8 @@ void Wallet::onHeightsRefreshed(bool success, quint64 daemonHeight, quint64 targ m_daemonBlockChainTargetHeight = targetHeight; if (success) { + m_heightRefreshFailures = 0; quint64 walletHeight = blockChainHeight(); - - qDebug() << "Heights - Wallet:" << walletHeight << "Daemon:" << daemonHeight << "Target:" << targetHeight; if (conf()->get(Config::dataSavingMode).toBool()) { this->syncStatusUpdated(daemonHeight, daemonHeight); @@ -555,26 +544,19 @@ void Wallet::onHeightsRefreshed(bool success, quint64 daemonHeight, quint64 targ this->syncStatusUpdated(walletHeight, daemonHeight); } - if (m_fullSyncRequested.load()) { - quint64 creationHeight = m_originalWalletCreationHeight; - if (walletHeight < targetHeight && walletHeight > creationHeight) { - setConnectionStatus(ConnectionStatus_Synchronizing); - qInfo() << "Full sync in progress:" << walletHeight << "/" << targetHeight; - } else if (walletHeight >= (targetHeight - 1)) { - m_fullSyncRequested.store(false); - setConnectionStatus(ConnectionStatus_Synchronized); - qInfo() << "Full sync completed"; - } else { - setConnectionStatus(ConnectionStatus_Synchronizing); - } - } else if (walletHeight < (targetHeight - 1)) { + if (walletHeight < (targetHeight - 1)) { setConnectionStatus(ConnectionStatus_Synchronizing); } else { setConnectionStatus(ConnectionStatus_Synchronized); } } } else { - if (m_connectionStatus == ConnectionStatus_Disconnected) { + m_heightRefreshFailures++; + if (m_heightRefreshFailures > 5) { + qWarning() << "Heights refresh failed" << m_heightRefreshFailures << "times - disconnecting to try new node"; + m_heightRefreshFailures = 0; + setConnectionStatus(ConnectionStatus_Disconnected); + } else if (m_connectionStatus == ConnectionStatus_Disconnected) { } else { qWarning() << "Heights refresh failed but maintaining connection status" << m_connectionStatus << "- will retry"; } @@ -1432,10 +1414,6 @@ void Wallet::setWalletCreationHeight(quint64 height) { m_wallet2->set_refresh_from_block_height(height); } -void Wallet::setFullSyncRequested(bool requested) { - m_fullSyncRequested.store(requested); -} - //! create a view only wallet bool Wallet::createViewOnly(const QString &path, const QString &password) const { // Create path diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 5c7dc056..bb4d6e4f 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -217,7 +217,6 @@ Q_OBJECT // ##### Synchronization (Refresh) ##### void startRefresh(); void pauseRefresh(); - void rescanBlockchainAsync(); //! Sync from specific height void syncFromHeight(quint64 height); @@ -412,7 +411,6 @@ Q_OBJECT quint64 getWalletCreationHeight() const; void setWalletCreationHeight(quint64 height); - void setFullSyncRequested(bool requested); //! Rescan spent outputs bool rescanSpent(); @@ -534,8 +532,8 @@ Q_OBJECT bool m_useSSL; bool m_newWallet = false; bool m_forceKeyImageSync = false; - std::atomic m_fullSyncRequested{false}; quint64 m_originalWalletCreationHeight = 0; + int m_heightRefreshFailures = 0; QTimer *m_storeTimer = nullptr; std::set m_selectedInputs; From 67d76027b8d51d9154fb883cb4501f76a2f5832d Mon Sep 17 00:00:00 2001 From: attentionisallyouneed Date: Fri, 16 Jan 2026 22:17:03 +0100 Subject: [PATCH 5/7] Restore getWalletCreationHeight --- src/libwalletqt/Wallet.cpp | 20 +++++++++----------- src/libwalletqt/Wallet.h | 17 ++++++++--------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 9fd62cc3..a828c583 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -56,8 +56,6 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent) m_walletListener = new WalletListenerImpl(this); m_walletImpl->setListener(m_walletListener); m_currentSubaddressAccount = getCacheAttribute(ATTRIBUTE_SUBADDRESS_ACCOUNT).toUInt(); - - m_originalWalletCreationHeight = m_walletImpl->getRefreshFromBlockHeight(); m_addressBookModel = new AddressBookModel(this, m_addressBook); m_subaddressModel = new SubaddressModel(this, m_subaddress); @@ -101,7 +99,7 @@ void Wallet::setConnectionStatus(ConnectionStatus value) { if (m_connectionStatus == value) { return; } - + m_connectionStatus = value; emit connectionStatusChanged(m_connectionStatus); } @@ -453,18 +451,18 @@ void Wallet::syncFromHeight(quint64 height) { void Wallet::syncDateRange(const QDateTime &startDate, const QDateTime &endDate) { // Monero genesis block timestamp: April 18, 2014 at 10:49:53 AM UTC QDateTime genesisTime = QDateTime::fromSecsSinceEpoch(1397818193, Qt::UTC); - + // Average Monero block time is ~2 minutes (120 seconds) const quint64 BLOCK_TIME_SECONDS = 120; - + // Calculate start height qint64 secondsFromGenesis = genesisTime.secsTo(startDate); quint64 startHeight = secondsFromGenesis > 0 ? (secondsFromGenesis / BLOCK_TIME_SECONDS) : 0; - - qInfo() << "Syncing date range from" << startDate.toString(Qt::ISODate) + + qInfo() << "Syncing date range from" << startDate.toString(Qt::ISODate) << "to" << endDate.toString(Qt::ISODate) << "| Estimated start height:" << startHeight; - + // Set the start height and begin sync // The wallet will sync until it catches up to the daemon this->syncFromHeight(startHeight); @@ -625,7 +623,7 @@ void Wallet::onRefreshed(bool success, const QString &message) { qCritical() << "Refresh failed with error:" << message; // Don't disconnect immediately - let the refresh thread retry // Only disconnect if we were already disconnected or connecting - if (m_connectionStatus == ConnectionStatus_Disconnected || + if (m_connectionStatus == ConnectionStatus_Disconnected || m_connectionStatus == ConnectionStatus_Connecting) { setConnectionStatus(ConnectionStatus_Disconnected); } else { @@ -700,7 +698,7 @@ bool Wallet::keyImageSyncNeeded(quint64 amount, bool sendAll) const { if (!this->viewOnly()) { return false; } - + if (sendAll) { return this->hasUnknownKeyImages(); } @@ -1407,7 +1405,7 @@ bool Wallet::setRingDatabase(const QString &path) { } quint64 Wallet::getWalletCreationHeight() const { - return m_originalWalletCreationHeight; + return m_walletImpl->getRefreshFromBlockHeight(); } void Wallet::setWalletCreationHeight(quint64 height) { diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index bb4d6e4f..07d7e5dd 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -142,7 +142,7 @@ Q_OBJECT bool isDeterministic() const; QString walletName() const; - + // ##### Balance ##### //! returns balance quint64 balance() const; @@ -153,7 +153,7 @@ Q_OBJECT quint64 unlockedBalance() const; quint64 unlockedBalance(quint32 accountIndex) const; quint64 unlockedBalanceAll() const; - + quint64 viewOnlyBalance(quint32 accountIndex) const; void updateBalance(); @@ -217,10 +217,10 @@ Q_OBJECT // ##### Synchronization (Refresh) ##### void startRefresh(); void pauseRefresh(); - + //! Sync from specific height void syncFromHeight(quint64 height); - + //! Sync from date range (converts dates to block heights) void syncDateRange(const QDateTime &startDate, const QDateTime &endDate); @@ -256,19 +256,19 @@ Q_OBJECT void setForceKeyImageSync(bool enabled); bool hasUnknownKeyImages() const; bool keyImageSyncNeeded(quint64 amount, bool sendAll) const; - + //! export/import key images bool exportKeyImages(const QString& path, bool all = false); bool exportKeyImagesToStr(std::string &keyImages, bool all = false); bool exportKeyImagesForOutputsFromStr(const std::string &outputs, std::string &keyImages); - + bool importKeyImages(const QString& path); bool importKeyImagesFromStr(const std::string &keyImages); //! export/import outputs bool exportOutputs(const QString& path, bool all = false); bool exportOutputsToStr(std::string& outputs, bool all); - + bool importOutputs(const QString& path); bool importOutputsFromStr(const std::string &outputs); @@ -348,7 +348,7 @@ Q_OBJECT //! Sign a transfer from file UnsignedTransaction * loadTxFile(const QString &fileName); UnsignedTransaction * loadUnsignedTransactionFromStr(const std::string &data); - + //! Load an unsigned transaction from a base64 encoded string UnsignedTransaction * loadTxFromBase64Str(const QString &unsigned_tx); @@ -532,7 +532,6 @@ Q_OBJECT bool m_useSSL; bool m_newWallet = false; bool m_forceKeyImageSync = false; - quint64 m_originalWalletCreationHeight = 0; int m_heightRefreshFailures = 0; QTimer *m_storeTimer = nullptr; From aee124228e10049c1d97f4d1bb315aee6ed6f130 Mon Sep 17 00:00:00 2001 From: attentionisallyouneed Date: Fri, 16 Jan 2026 22:23:33 +0100 Subject: [PATCH 6/7] Readded some deleted comments --- src/libwalletqt/Wallet.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index a828c583..19404211 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -418,8 +418,7 @@ void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 setTrustedDaemon(trustedDaemon); if (success) { - qDebug() << "init async finished"; - qDebug() << "Starting refresh"; + qDebug() << "init async finished - starting refresh"; startRefresh(); } }); @@ -487,6 +486,8 @@ void Wallet::startRefreshThread() { m_refreshNow = false; + // get daemonHeight and targetHeight + // daemonHeight and targetHeight will be 0 if call to get_info fails quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight(); bool success = daemonHeight > 0; @@ -501,6 +502,8 @@ void Wallet::startRefreshThread() if (conf()->get(Config::dataSavingMode).toBool()) { qInfo() << "Data Saving Mode: Skipping sync"; } else if (haveHeights) { + // Don't call refresh function if we don't have the daemon and target height + // We do this to prevent to UI from getting confused about the amount of blocks that are still remaining QMutexLocker locker(&m_asyncMutex); if (m_newWallet) { @@ -620,7 +623,8 @@ void Wallet::onUpdated() { void Wallet::onRefreshed(bool success, const QString &message) { if (!success) { - qCritical() << "Refresh failed with error:" << message; + // Something went wrong during refresh, in some cases we need to notify the user + qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it firstqCritical() << "Refresh failed with error:" << message; // Don't disconnect immediately - let the refresh thread retry // Only disconnect if we were already disconnected or connecting if (m_connectionStatus == ConnectionStatus_Disconnected || From c25469a1fd6deb07eb0e7aaa48d1e82046c9bc4a Mon Sep 17 00:00:00 2001 From: attentionisallyouneed Date: Fri, 16 Jan 2026 22:25:58 +0100 Subject: [PATCH 7/7] Fix some linting issues --- src/libwalletqt/Wallet.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 19404211..8efe437b 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -487,7 +487,7 @@ void Wallet::startRefreshThread() m_refreshNow = false; // get daemonHeight and targetHeight - // daemonHeight and targetHeight will be 0 if call to get_info fails + // daemonHeight and targetHeight will be 0 if call to get_info fails quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight(); bool success = daemonHeight > 0; @@ -507,6 +507,7 @@ void Wallet::startRefreshThread() QMutexLocker locker(&m_asyncMutex); if (m_newWallet) { + // Set blockheight to daemonHeight for newly created wallets to speed up initial sync m_walletImpl->setRefreshFromBlockHeight(daemonHeight); m_newWallet = false; } @@ -624,7 +625,7 @@ void Wallet::onUpdated() { void Wallet::onRefreshed(bool success, const QString &message) { if (!success) { // Something went wrong during refresh, in some cases we need to notify the user - qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it firstqCritical() << "Refresh failed with error:" << message; + qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it first // Don't disconnect immediately - let the refresh thread retry // Only disconnect if we were already disconnected or connecting if (m_connectionStatus == ConnectionStatus_Disconnected ||