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:
24
src/app.cpp
24
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();
|
||||
|
||||
@@ -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<void(bool, const std::string&)> 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::DaemonController> daemon_controller_;
|
||||
std::unique_ptr<daemon::XmrigManager> xmrig_manager_;
|
||||
util::AsyncTaskManager async_tasks_;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -188,6 +188,7 @@ LiteWalletController::LiteWalletController(WalletCapabilities capabilities,
|
||||
LiteClientBridge bridge,
|
||||
LiteWalletControllerOptions options)
|
||||
: bridge_(std::make_shared<LiteClientBridge>(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();
|
||||
|
||||
@@ -127,6 +127,9 @@ public:
|
||||
void setPersistCallback(std::function<void()> 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<LiteClientBridge> bridge_;
|
||||
std::string chainName_; // backend chain id (for walletExists); from connection settings
|
||||
LiteWalletLifecycleService lifecycle_;
|
||||
LiteWalletGateway gateway_;
|
||||
LiteSyncService sync_;
|
||||
|
||||
Reference in New Issue
Block a user