fix(wallet-switch): cluster B — daemon-switch robustness
From the wallet-switching audit: - Revert on failure (HIGH): switchToWallet persisted active_wallet_file before confirming the new daemon started, so a missing/corrupt wallet wedged across restarts. The switch worker now confirms dragonxd survives a grace period; on failure it flags the main thread (processWalletSwitchRevert), which restores the previous wallet file + re-scopes the vault + re-arms reconnect (settings writes stay on the main thread). - Cross-guard concurrent lifecycle ops (HIGH): switchToWallet now refuses during a rescan/repair (state_.sync.rescanning) or seed migration; rescan/repair now refuse while daemon_restarting_; and restartDaemonAfterEncryption now sets daemon_restarting_ (which also fixes a latent reconnect-to-a-stopped-daemon race during the encryption restart). So the switch/adopt/restart/rescan/repair/ encryption ops are mutually exclusive. - Quit during switch (MED): beginShutdown now joins the "Switch wallet" task (like the adopt task) so quitting can't orphan a freshly-started dragonxd. - Reset the daemon crash count on switch (LOW) so a prior crash-wedge can't block reconnecting to the new wallet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
src/app.cpp
16
src/app.cpp
@@ -649,6 +649,9 @@ void App::update()
|
||||
// capture_mode_ is only set for the offline full sweep, so the live tab-only sweep is unaffected.
|
||||
if (capture_mode_) return;
|
||||
|
||||
// If a wallet switch's daemon failed to start, revert to the previous wallet (main-thread-safe).
|
||||
processWalletSwitchRevert();
|
||||
|
||||
// Track user interaction for auto-lock
|
||||
if (io.MouseDelta.x != 0 || io.MouseDelta.y != 0 ||
|
||||
io.MouseClicked[0] || io.MouseClicked[1] ||
|
||||
@@ -4150,6 +4153,11 @@ void App::rescanBlockchain()
|
||||
ui::Notifications::instance().warning("A blockchain maintenance operation is already in progress.");
|
||||
return;
|
||||
}
|
||||
// Don't race a wallet switch / seed-adopt / encryption restart, which drive their own daemon stop/start.
|
||||
if (daemon_restarting_) {
|
||||
ui::Notifications::instance().warning("The node is busy restarting — try again in a moment.");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOGF("[App] Starting blockchain rescan - stopping daemon first\n");
|
||||
ui::Notifications::instance().info("Restarting daemon with -rescan flag...");
|
||||
@@ -4197,6 +4205,10 @@ void App::repairWallet()
|
||||
ui::Notifications::instance().warning("A blockchain maintenance operation is already in progress.");
|
||||
return;
|
||||
}
|
||||
if (daemon_restarting_) {
|
||||
ui::Notifications::instance().warning("The node is busy restarting — try again in a moment.");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOGF("[App] Starting wallet repair (-zapwallettxes=2) - stopping daemon first\n");
|
||||
ui::Notifications::instance().info("Restarting daemon with -zapwallettxes=2 (wallet repair)...");
|
||||
@@ -4370,6 +4382,10 @@ void App::beginShutdown()
|
||||
// the adopt task will NOT restart the daemon (it checks shutting_down_ before startEmbeddedDaemon).
|
||||
if (async_tasks_.isRunning("Adopt seed wallet"))
|
||||
async_tasks_.join("Adopt seed wallet");
|
||||
// The wallet-switch task also drives daemon stop/start — let it finish before shutdown touches the
|
||||
// daemon so it can't orphan a freshly-started dragonxd (it checks shutting_down_ before starting).
|
||||
if (async_tasks_.isRunning("Switch wallet"))
|
||||
async_tasks_.join("Switch wallet");
|
||||
|
||||
// Signal the RPC worker to stop accepting new tasks (non-blocking), and abort any call
|
||||
// already in flight so the later join() doesn't wait out a request timeout.
|
||||
|
||||
Reference in New Issue
Block a user