fix(wallet): surface silent save failures + stale-state cleanups (W5-1, W5-2, W6-1, W6-3)
P2 robustness batch (localized): - W5-1 (Med, lite): persistAfterBroadcast returned false on a persistent post-send/shield save failure, but both callers discarded it and it never logged — completely silent. It now liteLogs the failure (the spent note re-derives on the next sync, so it's a robustness gap, not fund loss). - W5-2 (Med, lite): the post-sync and post-rescan save results (in the detached scan threads) were ignored; both now liteLog on failure. LiteDiagnostics::log is mutex-guarded, so it's safe from those threads. - W6-1 (Med): WalletState::clear() didn't reset mining/pool_mining, so a wallet switch could briefly show the previous wallet's hashrate/blocks. Now reset in clear() (the daemon restarts on switch, so mining genuinely stops). - W6-3 (Low): AddressBook::load() cleared entries_ then threw on the first non-object array element — discarding EVERY contact. It now guards is_object() + per-entry try/catch, skipping and counting malformed entries. Build-clean; ctest 1/1. Remaining P2: W6-2 (refresh-staleness badge — needs UI, overlaps the diagnostics Foundation bundle). See docs/wallet-hardening.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -80,8 +80,10 @@ bool persistAfterBroadcast(LiteClientBridge& bridge)
|
||||
for (int attempt = 0; attempt < 2; ++attempt) {
|
||||
if (bridge.execute("save", "").ok) return true;
|
||||
}
|
||||
// Persistent failure: the spent note will be re-derived from the chain on the next sync,
|
||||
// so this is a robustness gap, not fund loss. (Retry handles the common transient case.)
|
||||
// Persistent failure: the spent note will be re-derived from the chain on the next sync, so this
|
||||
// is a robustness gap, not fund loss. Log it (W5-1) — both callers discard this return, so the
|
||||
// failure was previously completely silent.
|
||||
liteLog("save failed after send/shield — the wallet will re-derive it on the next sync");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -600,7 +602,8 @@ void LiteWalletController::startSync()
|
||||
// The backend does NOT auto-save after a sync, so persist the freshly-scanned wallet;
|
||||
// otherwise the next launch re-scans from the checkpoint (~30 min). Set `done` only
|
||||
// after the save so a syncComplete() observer sees a fully-persisted wallet.
|
||||
bridge->execute("save", "");
|
||||
if (!bridge->execute("save", "").ok) // W5-2: don't leave a failed post-sync save silent
|
||||
liteLog("save failed after sync — the next launch will re-scan from the checkpoint");
|
||||
}
|
||||
done->store(true);
|
||||
});
|
||||
@@ -631,7 +634,8 @@ bool LiteWalletController::startRescan()
|
||||
// `rescan` clears the wallet's synced block cache and re-downloads/re-scans from the
|
||||
// birthday height — a blocking, uninterruptible full scan, same as `sync`.
|
||||
bridge->execute("rescan", "");
|
||||
bridge->execute("save", ""); // backend doesn't auto-save after a rescan
|
||||
if (!bridge->execute("save", "").ok) // W5-2: don't leave a failed post-rescan save silent
|
||||
liteLog("save failed after rescan — the next launch will re-scan from the checkpoint");
|
||||
}
|
||||
done->store(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user