feat(lite): auto-open existing wallet on startup + gate full-node RPC refreshes

Auto-open: on the first update() tick (kept off init() so a slow
initialize_existing network call can't freeze startup before the window), if a
wallet file exists, open it. initialize_existing needs no passphrase — it loads
the file; a previously-synced + saved wallet resumes from its height (fast)
instead of rescanning from the checkpoint. Adds LiteWalletController::walletExists()
(bridge.walletExists on the connection's chain) + a chainName_ member.

RPC-refresh gating: the earlier connected=walletOpen() fix (so the wallet UI is
enabled in lite) had a side effect — the full-node periodic + per-page RPC
refreshes (mining/balance/peers/txs, and setCurrentPage's immediate refresh)
gate on state_.connected, so they began firing in lite and failing
("X error: Not connected"). Re-gate those on ACTUAL RPC connectivity
(rpc_ && rpc_->isConnected()) instead of the lite proxy. Full-node is unchanged
(state_.connected ⟺ rpc connected there); lite no longer issues any RPC.

Runtime-verified in WSLg with a pre-seeded wallet: app auto-opens (Starting
Mempool + sync begins), and "Not connected" / getMiningInfo / RPC-connect noise
all drop to 0 — a fully clean lite run. tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 16:29:27 -05:00
parent 89bd21018a
commit 76c2ac5db8
5 changed files with 39 additions and 6 deletions

View File

@@ -403,6 +403,16 @@ 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_) {
lite_autoopen_done_ = true;
if (!lite_wallet_->walletOpen() && lite_wallet_->walletExists()) {
lite_wallet_->openWallet(wallet::LiteWalletOpenRequest{});
}
}
// Lite has no RPC daemon (tryConnect() is a no-op in lite builds), so derive the app's
// "online" state — which gates the wallet UI via isConnected() — from the lite wallet.
// A wallet is open only after a successful backend init against the lite server, so this
@@ -441,11 +451,17 @@ void App::update()
using RefreshTimer = services::NetworkRefreshService::Timer;
network_refresh_.tick(io.DeltaTime);
// Full-node RPC refreshes gate on ACTUAL RPC connectivity, not state_.connected. In lite
// builds state_.connected is the lite-wallet "online" proxy (true when a wallet is open, to
// enable the wallet UI), but there is no RPC daemon — so RPC polls (mining/balance/peers/txs)
// must key off rpc_ being genuinely connected or they'd fire and fail ("Not connected").
const bool rpcConnected = rpc_ && rpc_->isConnected();
// Fast refresh (mining stats + daemon memory) every second
// Skip when wallet is locked — no need to poll, and queued tasks
// would delay the PIN unlock worker task.
if (network_refresh_.consumeDue(RefreshTimer::Fast)) {
if (state_.connected && !state_.isLocked()) {
if (rpcConnected && !state_.isLocked()) {
refreshMiningInfo();
// Poll getrescaninfo for rescan progress (if rescan flag is set)
@@ -620,9 +636,9 @@ void App::update()
}
}
// Poll pending z_sendmany operations for completion
// Poll pending z_sendmany operations for completion (full-node opid flow; lite has none)
if (network_refresh_.isDue(RefreshTimer::Opid) && !pending_opids_.empty()
&& state_.connected && fast_worker_ && !opid_poll_in_progress_) {
&& rpcConnected && fast_worker_ && !opid_poll_in_progress_) {
network_refresh_.reset(RefreshTimer::Opid);
auto opids = pending_opids_; // copy for worker thread
opid_poll_in_progress_ = true;
@@ -682,7 +698,7 @@ void App::update()
// Per-category refresh with tab-aware intervals
// Skip when wallet is locked — same reason as above.
if (state_.connected && !state_.isLocked()) {
if (rpcConnected && !state_.isLocked()) {
const bool walletDataPage = currentPageNeedsWalletDataRefresh();
if (network_refresh_.consumeDue(RefreshTimer::Core)) {
refreshCoreData();