diff --git a/src/app.cpp b/src/app.cpp index 7552aeb..40cc0ba 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -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(); diff --git a/src/app.h b/src/app.h index 3cb36bc..45af2dd 100644 --- a/src/app.h +++ b/src/app.h @@ -422,6 +422,9 @@ private: // Pending send_tab callback for an in-flight lite send (delivered in update() once the // controller's async broadcast result arrives). Only one lite send runs at a time. std::function lite_send_callback_; + // One-shot guard: auto-open an existing lite wallet on the first update() tick (kept off + // init() so a slow initialize_existing network call doesn't freeze startup before the window). + bool lite_autoopen_done_ = false; std::unique_ptr daemon_controller_; std::unique_ptr xmrig_manager_; util::AsyncTaskManager async_tasks_; diff --git a/src/app_network.cpp b/src/app_network.cpp index 8330535..12be317 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -565,8 +565,10 @@ void App::setCurrentPage(ui::NavPage page) applyRefreshPolicy(page); using RefreshTimer = services::NetworkRefreshService::Timer; - // Immediate refresh for the incoming tab's priority data - if (state_.connected && !state_.isLocked()) { + // Immediate refresh for the incoming tab's priority data. Gate on ACTUAL RPC connectivity + // (not state_.connected, which is the lite "online" proxy) — lite has no RPC daemon and the + // lite controller refreshes wallet data itself, so these full-node RPC polls must not fire. + if (rpc_ && rpc_->isConnected() && !state_.isLocked()) { using NP = ui::NavPage; switch (page) { case NP::Overview: diff --git a/src/wallet/lite_wallet_controller.cpp b/src/wallet/lite_wallet_controller.cpp index 70658fd..1310652 100644 --- a/src/wallet/lite_wallet_controller.cpp +++ b/src/wallet/lite_wallet_controller.cpp @@ -188,6 +188,7 @@ LiteWalletController::LiteWalletController(WalletCapabilities capabilities, LiteClientBridge bridge, LiteWalletControllerOptions options) : bridge_(std::make_shared(std::move(bridge))), + chainName_(connectionSettings.chainName), lifecycle_(capabilities, connectionSettings, bridge_.get(), LiteWalletLifecycleOptions{options.allowBridgeCalls}), gateway_(capabilities, connectionSettings, bridge_.get(), @@ -502,6 +503,13 @@ bool LiteWalletController::saveWallet() return bridge_->execute("save", "").ok; } +bool LiteWalletController::walletExists() const +{ + // chainName_ is always a backend-accepted value ("main"/"test"/"regtest"); litelib_wallet_ + // exists panics on unknown chains, but settings migration guarantees a valid one. + return bridge_ && bridge_->walletExists(chainName_); +} + bool LiteWalletController::refreshWalletState(dragonx::WalletState& state) { auto model = refreshModel(); diff --git a/src/wallet/lite_wallet_controller.h b/src/wallet/lite_wallet_controller.h index f289b78..3bf4349 100644 --- a/src/wallet/lite_wallet_controller.h +++ b/src/wallet/lite_wallet_controller.h @@ -127,6 +127,9 @@ public: void setPersistCallback(std::function callback) { persist_ = std::move(callback); } bool walletOpen() const { return walletOpen_; } + // True if a wallet file already exists on disk for this chain. Use to auto-open on startup + // (open via openWallet({}) needs no passphrase — initialize_existing just loads the file). + bool walletExists() const; const WalletBackendStatus& status() const { return status_; } LiteWalletLifecycleAvailability availability() const { return lifecycle_.availability(); } @@ -214,6 +217,7 @@ private: // sync_status separately), so concurrent execute() calls cannot corrupt state. A coarse // mutex would instead serialize sync against syncstatus polling and defeat the design. std::shared_ptr bridge_; + std::string chainName_; // backend chain id (for walletExists); from connection settings LiteWalletLifecycleService lifecycle_; LiteWalletGateway gateway_; LiteSyncService sync_;