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:
@@ -1,6 +1,7 @@
|
||||
#include "wallet/lite_result_parsers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -583,4 +584,32 @@ LiteSyncStatusParseResult parseLiteSyncStatusResponse(const json& value)
|
||||
return result;
|
||||
}
|
||||
|
||||
LiteSyncOpResult parseLiteSyncOpResponse(const std::string& text)
|
||||
{
|
||||
LiteSyncOpResult out;
|
||||
const json parsed = json::parse(text, nullptr, /*allow_exceptions=*/false);
|
||||
if (parsed.is_object()) {
|
||||
const auto it = parsed.find("result");
|
||||
if (it != parsed.end() && it->is_string() && it->get<std::string>() == "success") {
|
||||
out.ok = true;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
std::size_t b = 0, e = text.size();
|
||||
while (b < e && std::isspace(static_cast<unsigned char>(text[b]))) ++b;
|
||||
while (e > b && std::isspace(static_cast<unsigned char>(text[e - 1]))) --e;
|
||||
out.error = text.substr(b, std::min<std::size_t>(e - b, 300));
|
||||
if (out.error.empty()) out.error = "empty sync response";
|
||||
out.terminal = text.find("Reorg has exceeded") != std::string::npos;
|
||||
return out;
|
||||
}
|
||||
|
||||
int liteSyncRetryDelayMs(int attempt, int baseMs)
|
||||
{
|
||||
constexpr long long kMaxMs = 60000;
|
||||
long long ms = baseMs > 0 ? baseMs : 5000;
|
||||
for (int i = 1; i < attempt && ms < kMaxMs; ++i) ms *= 2;
|
||||
return static_cast<int>(ms < kMaxMs ? ms : kMaxMs);
|
||||
}
|
||||
|
||||
} // namespace dragonx::wallet
|
||||
Reference in New Issue
Block a user