feat(lite): async wallet open with server failover
Opening an existing lite wallet ran synchronously on the UI thread and used a
single server, so a dead/unreachable lightwalletd server froze startup for the
connect timeout and then stranded the wallet ("disconnected" spinner) — and the
DragonX lite servers are flaky (often several down at once).
Add LiteWalletController::beginOpenExisting() / pumpAsyncOpen(): the open runs on
a background thread (mirroring the sync/broadcast shared-lifetime pattern — it
captures only shared_ptrs + value copies, never `this`), trying the preferred
server first and then every other usable default until one succeeds. The main
thread finalizes the result (flips walletOpen, starts sync) or records the reason.
The rollout gate is still checked up-front on the main thread.
App: auto-open now calls beginOpenExisting() and pumps it each tick, retrying on
a 20s interval so a transient outage self-heals once a server returns; a failed
open surfaces its reason (notification + Network tab) instead of a silent spinner.
Tested: a fake bridge that fails specific servers exercises both
preferred-dead -> fallback-opens and all-dead -> fails-with-reason. Built clean
for full-node, lite, and Windows cross-compile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
44
src/app.cpp
44
src/app.cpp
@@ -480,25 +480,21 @@ void App::update()
|
||||
|
||||
// Apply any lite-wallet refresh the controller's background worker produced (main thread).
|
||||
if (lite_wallet_) {
|
||||
// Auto-open an existing wallet once, after the window is up. initialize_existing needs no
|
||||
// passphrase (it just loads the file); a previously-synced wallet then resumes from its
|
||||
// saved height (fast) rather than re-scanning from the checkpoint.
|
||||
if (!lite_autoopen_done_) {
|
||||
// Finalize any completed async open on the main thread (flips walletOpen / surfaces failure).
|
||||
lite_wallet_->pumpAsyncOpen();
|
||||
|
||||
// Auto-open an existing wallet asynchronously with server failover (initialize_existing
|
||||
// needs no passphrase — it just loads the file and contacts the server). Running it off
|
||||
// the UI thread means an unreachable server never freezes startup, and trying the other
|
||||
// default servers means one dead server no longer strands the wallet. Retried on an
|
||||
// interval so a transient outage self-heals once a server comes back.
|
||||
const double nowSecs = ImGui::GetTime();
|
||||
if (!lite_wallet_->walletOpen() && !lite_wallet_->openInProgress() &&
|
||||
lite_wallet_->walletExists() &&
|
||||
(!lite_autoopen_done_ || nowSecs - lite_open_last_attempt_ > 20.0)) {
|
||||
lite_autoopen_done_ = true;
|
||||
if (!lite_wallet_->walletOpen() && lite_wallet_->walletExists()) {
|
||||
const auto openResult = lite_wallet_->openWallet(wallet::LiteWalletOpenRequest{});
|
||||
// Surface why an existing wallet failed to open (e.g. the lightwalletd server
|
||||
// is unreachable) — otherwise the UI just shows a silent "disconnected" spinner.
|
||||
if (!openResult.ok && !openResult.walletReady) {
|
||||
lite_open_error_ = !openResult.error.empty() ? openResult.error
|
||||
: (!openResult.status.message.empty()
|
||||
? openResult.status.message
|
||||
: "Could not open the wallet");
|
||||
DEBUG_LOGF("[Lite] auto-open failed: %s\n", lite_open_error_.c_str());
|
||||
ui::Notifications::instance().error(
|
||||
std::string("Wallet open failed: ") + lite_open_error_, 8.0f);
|
||||
}
|
||||
}
|
||||
lite_open_last_attempt_ = nowSecs;
|
||||
lite_wallet_->beginOpenExisting();
|
||||
}
|
||||
|
||||
// Lite has no RPC daemon (tryConnect() is a no-op in lite builds), so derive the app's
|
||||
@@ -506,7 +502,17 @@ void App::update()
|
||||
// A wallet is open only after a successful backend init against the lite server, so this
|
||||
// is a non-blocking proxy for "lite backend operational".
|
||||
state_.connected = lite_wallet_->walletOpen();
|
||||
if (state_.connected) lite_open_error_.clear(); // opened successfully — clear any prior error
|
||||
if (state_.connected) {
|
||||
lite_open_error_.clear(); // opened successfully — clear any prior error
|
||||
} else {
|
||||
// Surface a failed open (e.g. server unreachable) once per distinct reason, so the
|
||||
// disconnected state isn't silent.
|
||||
const std::string& err = lite_wallet_->lastOpenError();
|
||||
if (!err.empty() && err != lite_open_error_) {
|
||||
lite_open_error_ = err;
|
||||
ui::Notifications::instance().error(std::string("Wallet open failed: ") + err, 8.0f);
|
||||
}
|
||||
}
|
||||
// Suppress the status bar's full-node connection-detail line in lite ("" and "Connected"
|
||||
// are both hidden); the connected/no-wallet indicator + sync status convey lite state.
|
||||
connection_status_ = state_.connected ? "Connected" : "";
|
||||
|
||||
Reference in New Issue
Block a user