From a5cd211e898e023ed4ab34f75ec3aabdb2d40b6a Mon Sep 17 00:00:00 2001 From: DanS Date: Tue, 27 Oct 2026 10:55:46 -0500 Subject: [PATCH] fix(wallet): correct Compact's behaviour with an external daemon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compact rewrites wallet.dat by restarting the node with -salvagewallet, which the wallet can only do for the EMBEDDED node it manages. When the wallet is connected to a dragonxd the user started themselves (use-embedded off, or it attached to an already-running node), daemon_controller_ is null — and compactWallet() fired the misleading "unavailable in lite build" warning even on a full-node wallet. Now: a precise App::canCompactWallet() (full-node AND embedded AND a live controller) disables the Compact button for an external/standalone daemon with a tooltip, and compactWallet() refuses with an accurate message (compact_needs_ embedded) that tells the user to enable "Use embedded dragonxd" or restart their dragonxd with -salvagewallet themselves. The lite-build path keeps its own message. New string compact_needs_embedded (English; 8 locales fall back). Build + tests + hygiene green. Co-Authored-By: Claude Opus 4.8 --- src/app.h | 5 +++++ src/app_network.cpp | 9 ++++++++- src/ui/pages/settings_page.cpp | 8 +++++++- src/util/i18n.cpp | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/app.h b/src/app.h index 657a8e9..7c6982b 100644 --- a/src/app.h +++ b/src/app.h @@ -805,6 +805,11 @@ public: // file in place, so a restart with -salvagewallet rewrites it into a fresh, compact file (keeping a // backup). -salvagewallet implies a full rescan, so this restarts the node and re-scans the chain. void compactWallet(); + // True when Compact can actually run: a full-node build whose EMBEDDED node the wallet manages (it + // restarts it with -salvagewallet). False for lite or when connected to an external/standalone dragonxd. + bool canCompactWallet() const { + return supportsFullNodeLifecycleActions() && isUsingEmbeddedDaemon() && daemon_controller_ != nullptr; + } // A newer daemon binary is installed but the running node is still the old one until a restart. void markDaemonRestartPending() { daemon_restart_pending_.store(true); } bool daemonRestartPending() const { return daemon_restart_pending_.load(); } diff --git a/src/app_network.cpp b/src/app_network.cpp index 623b50e..664efcf 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -6503,10 +6503,17 @@ void App::maybeWarnLargeWallet() void App::compactWallet() { auto& n = ui::Notifications::instance(); - if (!supportsFullNodeLifecycleActions() || !daemon_controller_) { + if (!supportsFullNodeLifecycleActions()) { n.warning(TR("appx_fullnode_lifecycle_unavailable_lite")); return; } + // Compacting rewrites wallet.dat by restarting the node with -salvagewallet — only possible when the + // wallet MANAGES the node (the embedded daemon). With an external dragonxd there is no controller to + // restart, so tell the user how to do it themselves instead of the misleading lite-build message. + if (!isUsingEmbeddedDaemon() || !daemon_controller_) { + n.warning(TR("compact_needs_embedded")); + return; + } // Consolidation frees internal BDB pages but never returns the space to the OS; -salvagewallet on the // next start rewrites wallet.dat into a fresh, compact file (and keeps the old one as a timestamped // .bak). It implies a full rescan, so restartDaemon() takes the node down and back up re-scanning. diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index c03de5f..251e877 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -2386,10 +2386,16 @@ void RenderSettingsPage(App* app) { if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_merge")); ImGui::SameLine(); // Compact reclaims the file space consolidation frees (BDB won't shrink in place). + // It rewrites wallet.dat by restarting the node with -salvagewallet, so it only works + // with the embedded node — disable it for an external dragonxd and explain on hover. + const bool canCompact = app->canCompactWallet(); + ImGui::BeginDisabled(!canCompact); if (material::ActionButton("##walletcompact", TR("wallet_size_compact"), ICON_MD_COMPRESS, material::ActionTier::Secondary)) s_settingsState.confirm_compact_wallet = true; - if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_wallet_size_compact")); + ImGui::EndDisabled(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + material::Tooltip("%s", TR(canCompact ? "tt_wallet_size_compact" : "compact_needs_embedded")); } // Row 3: folder buttons (their own row so the path gets the full width). diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 0cb7c6d..ee66886 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -885,6 +885,7 @@ void I18n::loadBuiltinEnglish() strings_["confirm_compact_wallet_msg"] = "Consolidating merged your notes, but the wallet file can't shrink on its own \xE2\x80\x94 the node has to rewrite it to reclaim the freed space. This restarts the node and re-scans the blockchain, which can take a while on a large wallet."; strings_["confirm_compact_wallet_safe"] = "Your funds and balance are safe \xE2\x80\x94 a timestamped backup of the current wallet is kept."; strings_["compact_wallet_confirm"] = "Compact"; + strings_["compact_needs_embedded"] = "Compacting rewrites wallet.dat, which the wallet can only do with its built-in node. You're connected to a dragonxd you started yourself \xE2\x80\x94 turn on \"Use embedded dragonxd\" in Settings, or restart your dragonxd with -salvagewallet to reclaim the space."; strings_["settings_debug_changed"] = "Debug categories changed \xe2\x80\x94 restart daemon to apply"; strings_["settings_auto_detected"] = "Auto-detected from DRAGONX.conf"; strings_["settings_visual_effects"] = "Visual Effects";