feat(wallet): detect a corrupt target wallet on switch + offer -salvagewallet repair
When a switch fails, the failure modal now distinguishes a CORRUPT target wallet
from other failures and offers a one-click repair:
- The switch worker watermarks the node's captured console output before the
start and, if the node dies in init, scans this start's output for a corruption
signature ("Failed to rename … .bak" / "salvage failed" / "wallet.dat corrupt" /
"Error loading wallet") → sets switch_wallet_corrupt_.
- The Failed modal then shows an accurate "this wallet appears corrupt" message
(instead of the generic "Couldn't open that wallet") plus a "Try to repair
(salvage)" button that retries the switch with the target node started under
-salvagewallet (recovers readable keypairs; implies -rescan).
- EmbeddedDaemon::setSalvageOnNextStart (one-shot, precedence salvage > zap >
rescan) + controller forwarder; switchToWallet gains a salvage arg.
Salvage operates only on the corrupt target (never the good wallet, no fund
movement). Adversarially verified: output isolation, one-shot lifecycle, state
handling, re-entrancy, no success-path regression.
i18n (EN + 8 languages) + 2 new CJK glyphs baked into the subset font.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,11 @@ void DaemonController::setZapOnNextStart(bool enabled)
|
||||
daemon_->setZapOnNextStart(enabled);
|
||||
}
|
||||
|
||||
void DaemonController::setSalvageOnNextStart(bool enabled)
|
||||
{
|
||||
daemon_->setSalvageOnNextStart(enabled);
|
||||
}
|
||||
|
||||
bool DaemonController::zapOnNextStart() const
|
||||
{
|
||||
return daemon_->zapOnNextStart();
|
||||
|
||||
@@ -107,6 +107,7 @@ public:
|
||||
bool rescanOnNextStart() const;
|
||||
void setZapOnNextStart(bool enabled);
|
||||
bool zapOnNextStart() const;
|
||||
void setSalvageOnNextStart(bool enabled);
|
||||
|
||||
static ShutdownDecision evaluateShutdownPolicy(bool hasDaemon,
|
||||
bool externalDaemonDetected,
|
||||
|
||||
@@ -524,9 +524,16 @@ bool EmbeddedDaemon::start(const std::string& binary_path)
|
||||
args.push_back("-wallet=" + wallet_file_);
|
||||
}
|
||||
|
||||
// Add wallet-repair flag if requested (one-shot). -zapwallettxes=2 wipes all wallet tx/note
|
||||
// records and rebuilds them from the chain; it implies -rescan, so don't also pass -rescan.
|
||||
if (zap_on_next_start_.exchange(false)) {
|
||||
// Add wallet-repair flag if requested (one-shot). Precedence: salvage > zap > rescan; each implies a
|
||||
// rescan in the daemon, so we don't stack them.
|
||||
if (salvage_on_next_start_.exchange(false)) {
|
||||
// -salvagewallet recovers readable keypairs from a corrupt wallet.dat; the daemon then implies -rescan.
|
||||
DEBUG_LOGF("[INFO] Adding -salvagewallet flag to recover a corrupt wallet\n");
|
||||
args.push_back("-salvagewallet");
|
||||
zap_on_next_start_.store(false);
|
||||
rescan_on_next_start_.store(false);
|
||||
} else if (zap_on_next_start_.exchange(false)) {
|
||||
// -zapwallettxes=2 wipes all wallet tx/note records and rebuilds them from the chain (implies -rescan).
|
||||
DEBUG_LOGF("[INFO] Adding -zapwallettxes=2 flag for wallet repair (zap & rebuild)\n");
|
||||
args.push_back("-zapwallettxes=2");
|
||||
rescan_on_next_start_.store(false); // implied by zap; avoid redundant -rescan
|
||||
|
||||
@@ -201,6 +201,11 @@ public:
|
||||
void setZapOnNextStart(bool v) { zap_on_next_start_ = v; }
|
||||
bool zapOnNextStart() const { return zap_on_next_start_.load(); }
|
||||
|
||||
// -salvagewallet: attempt to recover keys from a corrupt wallet.dat on startup (implies -rescan in
|
||||
// the daemon). One-shot, consumed on the next start. Used to repair a wallet a switch flagged corrupt.
|
||||
void setSalvageOnNextStart(bool v) { salvage_on_next_start_ = v; }
|
||||
bool salvageOnNextStart() const { return salvage_on_next_start_.load(); }
|
||||
|
||||
/**
|
||||
* @brief One-shot isolated-datadir override for the NEXT start(): run the daemon against a
|
||||
* different datadir (with its own DRAGONX.conf) plus the given extra args. Used by the
|
||||
@@ -274,6 +279,7 @@ private:
|
||||
std::atomic<int> crash_count_{0}; // consecutive crash counter
|
||||
std::atomic<bool> rescan_on_next_start_{false}; // -rescan flag for next start
|
||||
std::atomic<bool> zap_on_next_start_{false}; // -zapwallettxes=2 flag for next start
|
||||
std::atomic<bool> salvage_on_next_start_{false}; // -salvagewallet flag for next start
|
||||
std::string override_datadir_; // one-shot: -datadir for the next start
|
||||
std::vector<std::string> override_extra_args_; // one-shot: extra args for the next start
|
||||
bool skip_port_check_ = false; // isolated instance on a non-default port
|
||||
|
||||
Reference in New Issue
Block a user