fix(wallet): correct Compact's behaviour with an external daemon

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 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 10:55:46 -05:00
co-authored by Claude Opus 4.8
parent 0ebd1ac5d5
commit a5cd211e89
4 changed files with 21 additions and 2 deletions
+5
View File
@@ -805,6 +805,11 @@ public:
// file in place, so a restart with -salvagewallet rewrites it into a fresh, compact file (keeping a // 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. // backup). -salvagewallet implies a full rescan, so this restarts the node and re-scans the chain.
void compactWallet(); 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. // 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); } void markDaemonRestartPending() { daemon_restart_pending_.store(true); }
bool daemonRestartPending() const { return daemon_restart_pending_.load(); } bool daemonRestartPending() const { return daemon_restart_pending_.load(); }
+8 -1
View File
@@ -6503,10 +6503,17 @@ void App::maybeWarnLargeWallet()
void App::compactWallet() void App::compactWallet()
{ {
auto& n = ui::Notifications::instance(); auto& n = ui::Notifications::instance();
if (!supportsFullNodeLifecycleActions() || !daemon_controller_) { if (!supportsFullNodeLifecycleActions()) {
n.warning(TR("appx_fullnode_lifecycle_unavailable_lite")); n.warning(TR("appx_fullnode_lifecycle_unavailable_lite"));
return; 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 // 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 // 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. // .bak). It implies a full rescan, so restartDaemon() takes the node down and back up re-scanning.
+7 -1
View File
@@ -2386,10 +2386,16 @@ void RenderSettingsPage(App* app) {
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_merge")); if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_merge"));
ImGui::SameLine(); ImGui::SameLine();
// Compact reclaims the file space consolidation frees (BDB won't shrink in place). // 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"), if (material::ActionButton("##walletcompact", TR("wallet_size_compact"),
ICON_MD_COMPRESS, material::ActionTier::Secondary)) ICON_MD_COMPRESS, material::ActionTier::Secondary))
s_settingsState.confirm_compact_wallet = true; 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). // Row 3: folder buttons (their own row so the path gets the full width).
+1
View File
@@ -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_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_["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_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_debug_changed"] = "Debug categories changed \xe2\x80\x94 restart daemon to apply";
strings_["settings_auto_detected"] = "Auto-detected from DRAGONX.conf"; strings_["settings_auto_detected"] = "Auto-detected from DRAGONX.conf";
strings_["settings_visual_effects"] = "Visual Effects"; strings_["settings_visual_effects"] = "Visual Effects";