fix(lite): honor the backend sync result with retry and a failed state
A failed backend sync was silently swallowed: the reply was never inspected, so a sync that errored out left the wallet showing a "syncing" state that never completed and never reported a problem. - The sync and rescan replies are now parsed. On failure the partial progress is saved and the sync is retried with exponential back-off (5s doubling up to 60s, six attempts in all), then an explicit failed state is published: done, sync not complete, and the backend's error text carried through. - A failed rescan is retried as a plain sync, because a rescan has already cleared the wallet and re-running it would throw away the partial progress just saved. - A terminal error, such as a reorg deeper than the wallet can unwind, stops the retries immediately instead of waiting out the attempts. - Recovery is observed, not guessed: once a later refresh's server-bound reads all succeed, the failed state clears itself. Behaviour change: a sync failure now surfaces to the user as a failed state with a message, instead of a frozen "syncing", and the client retries on its own with back-off. Tests: a parser case for the success, failure, and terminal replies, and fake-backend controller cases for retrying after a failure, giving up after the attempt cap, a terminal reorg stopping at once, a failed rescan retrying as sync, cancel stopping the back-off, and the failed state self-healing on a good refresh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,10 @@ inline bool g_liteFakeWalletExists = true;
|
||||
inline bool g_liteFakeServerOnline = true;
|
||||
inline bool g_liteFakeShutdownCalled = false;
|
||||
inline std::atomic<bool> g_liteFakeSyncBlock{false}; // when true, the "sync" command blocks
|
||||
inline std::atomic<int> g_liteFakeSyncFailures{0}; // next N "sync" commands fail with the real plain-text shape
|
||||
inline std::atomic<bool> g_liteFakeSyncTerminal{false}; // "sync" returns the terminal reorg error
|
||||
inline std::atomic<int> g_liteFakeRescanFailures{0}; // next N "rescan" commands fail
|
||||
inline std::atomic<long> g_liteFakeSyncCalls{0};
|
||||
inline std::atomic<bool> g_liteFakeBadBalance{false}; // when true, "balance" returns invalid JSON
|
||||
inline std::atomic<bool> g_liteFakeSendFails{false}; // when true, "send"/"shield" return {"error":..}
|
||||
inline std::atomic<long> g_liteFakeSaveCount{0}; // counts "save" commands (persistence checks)
|
||||
@@ -96,6 +100,10 @@ inline void resetLiteFakeCounters()
|
||||
g_liteFakeBlockImport = false;
|
||||
g_liteFakeImportFails = false;
|
||||
g_liteFakeImportEncrypted = false;
|
||||
g_liteFakeSyncFailures = 0;
|
||||
g_liteFakeSyncTerminal = false;
|
||||
g_liteFakeRescanFailures = 0;
|
||||
g_liteFakeSyncCalls = 0;
|
||||
g_liteFakeLogEnabled = false;
|
||||
(void)liteFakeTakeLog();
|
||||
}
|
||||
@@ -184,6 +192,13 @@ inline char* liteFakeExecute(const char* command, const char* args)
|
||||
// Simulate the real backend's blocking full sync when requested, so tests can
|
||||
// verify shutdown doesn't hang on an in-flight sync.
|
||||
while (g_liteFakeSyncBlock.load()) std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
++g_liteFakeSyncCalls;
|
||||
if (g_liteFakeSyncTerminal.load()) return liteFakeDup("Reorg has exceeded 100 blocks. Aborting.");
|
||||
if (g_liteFakeSyncFailures.load() > 0) { --g_liteFakeSyncFailures; return liteFakeDup("Error getting latest block: simulated connect failure"); }
|
||||
return liteFakeDup("{\"result\":\"success\"}");
|
||||
}
|
||||
if (std::strcmp(c, "rescan") == 0) {
|
||||
if (g_liteFakeRescanFailures.load() > 0) { --g_liteFakeRescanFailures; return liteFakeDup("Error fetching blocks: simulated"); }
|
||||
return liteFakeDup("{\"result\":\"success\"}");
|
||||
}
|
||||
if (std::strcmp(c, "syncstatus") == 0) // real backend shape: "syncing" is a string
|
||||
|
||||
Reference in New Issue
Block a user